Search code examples
androidjava-8retrolambda

Android RetroLamda implementation prob


I am new in using RetroLamda in Android development. I have been learnt that it depends on the parameter type to select the method. I have interface like below:

interface OnCallListener {
  onSuccess(String msg);
  onError(String msg);
}

Now in common implementation:

test.SetOnCallListener(new OncallListener(){
  public void onSuccess(String msg){
  ......
  }
  public void onError(String msg){
  .....
  }

});

How to handle in RetroLamda expression this type of cases where two method has same input type?


Solution

  • Short answer:

    You can use default methods and subclass your listeners to be responsible for only one type of event.

    Long answer:

    The answer I'm giving is more suited to migrating normal Java listeners that bear similar qualities: ones with interfaces having multiple abstract methods, occasionally those will contain unnecessary amounts of boilerplate as well, because user would only be interested in handling one specific event rather than all of them at once.

    Using them with Retrolambda bears some amount of tradeoffs that you may or may not be willing to take. More on this here and here.

    Basic idea: to make listener parameter a valid target for implementing with lambda, one has to ensure that whatever the listener target class is, it should have one and only one abstract method. Considering the initial class:

    public interface CallListener<T> {
      void onSuccess(T result);
      void onError(String errorMsg);
    }
    

    It has several abstract methods, so to make it valid assignment target for lambda, we have to "implement" all but one method. We do this by using default methods:

    public class CallListeners {
      public static interface SuccessListener<T> extends CallListener<T> {
        // we implement onError in such way that listener would ignore error event
        default void onError(String _ignore) {}
      }
      public static interface ErrorListener<T> extends CallListener<T> {
        default void onSuccess(T result) {}
      }
    
      // Methods to "help" compiler infer correct listener type
      public void <T> Call<T> success(SuccessListener<T> listener) {
        return listener;
      }
      public void <T> Call<T> error(ErrorListener<T> listener) {
        return listener;
      }
    }
    

    After that, you can use all of the above like this:

    test.SetOnCallListener(
      CallListeners.success(argument -> /*your implementation here*/));
    

    Alternatively:

    You can make a concrete class that allows plugging various (potentially reusable) implementations of its methods, and has utility factory methods where all but one of event listeners are implemented by empty operation:

    public class CallListenerImpl<T> implements CallListener<T> {
      private Consumer<T> success; // java.util.function.Consumer
      private Consumer<String> error;
      public CallListenerImpl(Consumer<? super T> succ, Consumer<String> err) {
        success = succ; error = err;
      }
      void onSuccess(T result) {
        success.accept(result);
      }
      void onError(String err) {
        error.accept(err);
      }
    
      // Sugary stuffs:
      public static <T> CallListenerImpl<T> success(Consumer<T> succ) {
        return new CallListenerImpl<>(succ, noOp());
      }
      public static <T> CallListenerImpl<T> error(Consumer<String> err) {
        return new CallListenerImpl<>(noOp(), err);
      }
      private static <T> Consumer<T> noOp() {
        return a -> {};
      }
    }