how to call a lambda of unknown type

  • A+
Category:Languages

Consider we have a set of variables containing references to methods:

public double m2(String s, int n){return n;} Runnable r = ()->{}; Consumer<String> c1 = System.out::println; BiFunction<String, Integer, Double> f2 = this::m2; 

We can call them in different ways, depending on concrete variable type:

    r.run();     c1.accept("Hello");     Object res = f2.apply("Hello", 1); 

Now, I need a global method to invoke any type of referenced methods:

public static Object call(Object lambda, Object... args) {...} Object res0 = call(r); // returns null Object res1 = call(c1, "Hello"); Object res2 = call(f2, "Hello", 1) 

How to implement this method? The set of possible functional types of the parameter lambda is not limited.

 


You need to identify the type of the object and call/invoke it appropriately. The following method checks for Runnable, Callable, Method and anything that is annotated with FunctionalInterface. If the type is Method it will assume that the first parameter is the object on which to invoke the method on.

public Object callWhatever(final Object o, final Object... params) throws Exception {     if (o instanceof Runnable) {         ((Runnable)o).run();         return null;     }      if (o instanceof Callable) {         return ((Callable<?>)o).call();     }      if (o instanceof Method) {         return ((Method)o).invoke(params[0], Arrays.copyOfRange(params, 1, params.length));     }      final Method method = getMethodForFunctionalInterface(o);     if (method != null) {         return method.invoke(o, params);     }      throw new InvalidParameterException("Object of type " + o.getClass() +  " is not callable!"); } 

The method getMethodForFunctionalInterface uses reflection to walk through the passed object's classes and interfaces, looking for a FunctionalInterface annotation. This annotation is present on all Java8 functionals, lambdas etc. It will then look for the method that needs to be invoked and return it (functional interfaces are allowed to have only one public abstract method, so it's easy to find it):

public Method getMethodForFunctionalInterface(final Object o) {     Class<?> clazz = o.getClass();     while (clazz != null) {         for (final Class<?> interfaze : clazz.getInterfaces()) {             if (interfaze.isAnnotationPresent(FunctionalInterface.class)) {                 for (final Method method : interfaze.getDeclaredMethods()) {                     if (Modifier.isAbstract(method.getModifiers())) {                         return method;                     }                 }             }         }         clazz = clazz.getSuperclass();     }     return null; } 

Please note that custom functional interfaces without the FunctionalInterface annotation are allowed by Java and will not be detected by this method.

Now you can call whatever:

public void test() throws Exception {     final Runnable runnable = ()->{System.out.println("r called");};     final Callable<Integer> callable = ()->{System.out.println("c called"); return 123456; };     final Consumer<String> consumer = System.out::println;     final BiFunction<String, Integer, Double> bifunction = this::m2;      final Object test1 = callWhatever(runnable);     final Object test2 = callWhatever(callable);     final Object test3 = callWhatever(consumer, "Hello World");     final Object test4 = callWhatever(bifunction, "Hello", 123);     final Object test5 = callWhatever(this.getClass().getDeclaredMethod("m2", String.class, int.class), this, "Hello", 234);      System.out.println(test1);     System.out.println(test2);     System.out.println(test3);     System.out.println(test4);     System.out.println(test5); } 

Output:

r called
c called
Hello World
null
123456
null
123.0
234.0

Comment

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: