Identifying method reference to a non static java method represented as Bi-function

64 Views Asked by At

Consider a class, say

Class Abc {
private String extraInfo;
pubilc Abc(String extraInfo) {
this.extraInfo = extraInfo;
}

// Assume Input1, Input2 extend the same class 'Input`


public Input2 method1 (Input1 input) {
 // performs some action based on the input. 
}

public Input1 method2 (Input2 input) {

}
}

What I'm trying to do is write a common method that takes in refrence to these methods as bifuction and want to identify whether the function reference is of method1 or method2.

eg.

void testSuccess (Bifunction<Abc, I, O> func) {
val obj = new Abc("text");
func.apply(obj, decideInput(func));
}
  1. I'm trying to write decideInput which takes in the method ref and returns Input1 type object if the ref is for method1 and Input2 if ref is for method2.

  2. Also would it be a problem writing decideInput if Input1, Input2 do not extend the same class Input.

I know that the func is a lambda and that it could cause identifying the actual reference.

2

There are 2 best solutions below

1
user699848 On

method1 and method2 both are of type Function. Not sure why you are trying to use BiFunction

BiFunction would be used for a method that takes in two arguments.

1
Sweeper On

You are not supposed to know what method a method reference is referencing. If there is a way (and I doubt that), it would be implementation dependent.

The way that method references are implemented, is that they evaluate to an instance of a hidden class, This hidden class is generated at runtime, and would implement the functional interface.

Suppose you have

BiFunction<Abc, Input1, Input2> f = Abc::method1;

this would create a hidden class with apply implemented like this:

public Object apply(Object a, Object b) {
    ((Abc)a).method1((Input1)b);
}
// of course, the runtime wouldn't generate Java source code - this is for demonstration only

Note that the generic parameters are all erased.

To find out which method the method reference is referencing at runtime, you would need to go into the implementation of the apply method in the hidden class to find out. You might be able to write the hidden class to a .class file (honestly I'm not even sure if this is possible), then read that file to find how the apply method is implemented.

Another attempt at trying to get the parameter types would be try to getGenericInterfaces of the hidden class, then use getActualTypeArguments to find out the second type argument of BiFunction. Whether this would work would depend on whether the implementation of the JVM inserts generic type information into the generated hidden class, and I've never seen any implementation do this.


If you just want to accept a fixed set of method references, I would suggest using an enum to represent that set of methods, and switch on the enum in testSuccess and call those methods.

If you want to accept any one-argument method on Abc, you would need a an object that not only wraps the method, but also the type of the argument it takes, e.g.:

abstract class MethodOfAbc {
    private final Class<?> parameterType;
    public abstract Object apply(Abc abc, Object o);

    // optionally,
    // private String methodName;
    
    public MethodOfAbc(Class<?> parameterType) {
        this.parameterType = parameterType;
    }
    public Class<?> getParameterType() { return parameterType; }
    public static <T, U> MethodOfAbc make(BiFunction<Abc, T, U> f, Class<T> parameterType) {
        return new MethodOfAbc(parameterType) {
            @Override
            public Object apply(Abc abc, Object o) {
                return f.apply(abc, (T)o);
            }
        };
    }
}

// Usage:
testSuccess(MethodOfAbc.make(Abc::method1, Input1.class));

Now you can check the type by inspecting getParameterType(), and deciding the input.