Reputation: 23
I am trying to create a method reference with the variable, which holds method name for some method from an object:
SomeClass obj = new SomeClass();
String methodName = "someMethod";
I am looking for way to create exactly obj::someMethod
, but using variable methodName for this. Is it possible?
I know how to create functional interface instance from methodName
and obj
:
() -> {
try {
return obj.getClass().getMethod(methodName).invoke(obj);
} catch (NoSuchMethodException | IllegalAccessException e) {
return null;
}
};
but I am wondering is this can be done in more shorthand way.
Upvotes: 2
Views: 121
Reputation: 298113
If you strive for brevity rather than performance, there are Expression
and Statement
since Java 1.4.
Object obj="example";
String methodName="substring";
Object[] arg={ 2, 5 };
try {
Object result=new Expression(obj, methodName, arg).getValue();
new Statement(System.out, "println", new Object[]{ result }).execute();
} catch (Exception ex) {
Logger.getLogger(YourClass.class.getName()).log(Level.SEVERE, null, ex);
}
But if you want to use them in the context of the standard function interfaces, which don’t allow checked exceptions, the exception handling will dominate the source code.
You can bind a reflectively acquired method to a functional interface even under Java 7:
Object obj="example";
String methodName="substring";
Object[] arg={ 2, 5 };
Supplier<String> s;
Consumer<String> c;
try {
MethodHandle mh=MethodHandles.insertArguments(
MethodHandles.lookup().bind(obj, methodName,
MethodType.methodType(String.class, int.class, int.class)),
0, arg);
s = MethodHandleProxies.asInterfaceInstance(Supplier.class, mh);
mh=MethodHandles.lookup().bind(System.out, "println",
MethodType.methodType(void.class, String.class));
c = MethodHandleProxies.asInterfaceInstance(Consumer.class, mh);
} catch(NoSuchMethodException | IllegalAccessException ex) {
Logger.getLogger(YourClass.class.getName()).log(Level.SEVERE, null, ex);
return;
}
String result=s.get();
c.accept(result);
This is not shorter, but avoids performing the reflective lookup in each function evaluation.
Potentially more efficient is to use the LambdaMetafactory
introduced in Java 8, which is the back-end of the lambda expressions and method references at runtime.
Object obj="example";
String methodName="substring";
Object[] arg={ 2, 5 };
Supplier<String> s;
Consumer<String> c;
try {
final MethodHandles.Lookup lookup = MethodHandles.lookup();
MethodHandle mh=lookup.findVirtual(String.class, methodName,
MethodType.methodType(String.class, int.class, int.class));
s = (Supplier<String>)LambdaMetafactory.metafactory(lookup, "get",
mh.type().changeReturnType(Supplier.class),
MethodType.methodType(Object.class), mh, MethodType.methodType(String.class))
.getTarget().bindTo(obj).invokeWithArguments(arg);
mh=MethodHandles.lookup().findVirtual(PrintStream.class, "println",
MethodType.methodType(void.class, String.class));
c = (Consumer<String>)LambdaMetafactory.metafactory(lookup, "accept",
MethodType.methodType(Consumer.class, PrintStream.class),
MethodType.methodType(void.class, Object.class), mh,
MethodType.methodType(void.class, String.class))
.getTarget().bindTo(System.out).invokeExact();
} catch(Throwable ex) {
Logger.getLogger(YourClass.class.getName()).log(Level.SEVERE, null, ex);
return;
}
String result=s.get();
c.accept(result);
This has a higher creation complexity, but the subsequent execution of the functions will have an efficiency on par with compile-time method references as there is no technical difference anymore.
Upvotes: 1
Reputation: 272467
This is a very rare case - it's highly unlikely that the Java8 syntax sugar has been optimised for this. In particular, it would be impossible for the usual compile-time type checking to occur (remember that a method reference is just syntax sugar for an anonymous class that adheres to a particular type contract).
If this pattern is common in your codebase (hopefully it's not!), you could just move it to, say, a static utility method, and then do () -> Utils.invoke(obj, methodName)
.
Upvotes: 0