Is it possible to convert a method reference (e.g. SomeClass::someMethod
) to a MethodHandle
instance? I want the benefits of compile-time checking (ensuring that the class and method exists) as well as the ability to introspect the method using the MethodHandle
API.
Use-case: I've got code that needs to execute if and only if the request was not triggered by a specific method (to avoid endless recursion). I want a compile-time check to ensure the class/method exists but a runtime check to compare the caller to the method.
So to recap: Is it possible to convert a method reference to a MethodHandle
?
Well, if you can afford the additional overhead and security implications, you can use a Serializable
functional interface
and decode the serialized form of the method reference instance to find the target like demonstrated in this answer or brought up again with this question and its answers.
However, you should really rethink your software design. “Avoiding endless recursion” shouldn’t be fixed by decoding some kind of parameter object, especially not if your assumption is, that this actual argument value represents the caller of your method. How would you ever enforce this strange relationship?
Even a simple code change like referencing a method which delegates to the other method would break your check. Here is a simple example showing the subtle problems with your approach:
public class SimpleTest {
public static void main(String... arg) {
run(SimpleTest::process);
}
static void run(BiConsumer<Object,Object> c) {
c.accept("foo", "bar");
}
static void process(Object... arg) {
Thread.dumpStack();
}
}
When running this program it will print something like:
java.lang.Exception: Stack trace
at java.lang.Thread.dumpStack(Thread.java:1329)
at SimpleTest.process(SimpleTest.java:16)
at SimpleTest.lambda$MR$main$process$a9318f35$1(SimpleTest.java:10)
at SimpleTest$$Lambda$1/26852690.accept(Unknown Source)
at SimpleTest.run(SimpleTest.java:13)
at SimpleTest.main(SimpleTest.java:10)
showing that the method reference within the generated instance is not the expected SimpleTest::process
but instead SimpleTest::lambda$MR$main$process$a9318f35$1
which will eventually invoke process
. The reason is that some operations (here varargs processing) are not performed by the generated interface
instance but a synthetic method instead, just like you had written run((a,b)-> SimpleTest.process(a,b))
. The only difference is the name of the synthetic method.
You shouldn’t design software relying on such fragile introspection. If you want to avoid recursion, a simple ThreadLocal
flag telling whether you are already inside your specific method would do the job. But it might be worth asking yourself why your API is provoking endless recursion in the first place; there seems to be something fundamentally wrong…
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With