Can I convert a method reference to a MethodHandle?

Is it possible to convert a method reference (e.g. SomeClass::someMethod ) into an instance of MethodHandle ? I want to take advantage of compile-time checking (ensuring that the class and method exist), as well as the ability to introspect the method using the MethodHandle API.

Use case: I have a code that needs to be executed if and only if the request has not been called by a specific method (to avoid infinite recursion). I want a compile-time check to ensure that the class / method exists, but a run-time check to compare the caller with the method.

So to: Is it possible to convert a method reference to a MethodHandle ?

+9
java java-8 method-reference methodhandle
Oct 30 '14 at 9:34
source share
1 answer

Well, if you can afford the extra overhead and security implications, you can use the Serializable functional interface and decode the serialized form of the method link instance to find the target, as shown in this answer or raised with this question and its answers again.

However, you must really rethink your software design. "Preventing infinite recursion" should not be fixed by decoding any parameter object, especially if your assumption is that this actual value of the argument is the calling object of your method. How would you never apply this weird relationship?

Even a simple code change, such as a reference to a method that delegates to another method, will violate your check. Here is a simple example showing subtle issues 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 you run 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 in the generated instance is not the expected SimpleTest::process , but instead SimpleTest::lambda$MR$main$process$a9318f35$1 , which ultimately calls process . The reason is that some operations (here varargs processing) are not performed by the generated interface instance, but instead by the synthetic method, like you wrote run((a,b)-> SimpleTest.process(a,b)) . The only difference is the name of the synthetic method.

You should not design software based on such a fragile introspection. If you want to avoid recursion, a simple ThreadLocal tag indicates whether you will already do the work in your specific method. But perhaps you should ask yourself why your API provokes infinite recursion in the first place; something seems fundamentally wrong ...

+4
Oct 30 '14 at 15:21
source share



All Articles