Convert MethodHandle to method reference (here Function)
Asked Answered
B

3

16
MethodType methodType = MethodType.methodType(void.class, ByteBuffer.class);
MethodHandle handle = MethodHandles.publicLookup().findConstructor(type, methodType);

Function<ByteBuffer, Object> = handle; // ???

Is it possible to get the last assignment work? The inverted way does not work: Is it possible to convert method reference to MethodHandle?

Here another and copy-pastable example:

new Integer("123");

MethodType methodType = MethodType.methodType(void.class, String.class);
MethodHandle handle = MethodHandles.publicLookup().findConstructor(Integer.class, methodType);

Function<String, Integer> function1 = Integer::new;
Function<String, Integer> function2 = handle.toLambda(); // ???
Boccioni answered 11/12, 2014 at 7:40 Comment(2)
https://mcmap.net/q/748530/-get-a-list-of-classes-lambdasAdulate
Holger, thanks for pointing me to the LambdaMetafactory.Boccioni
A
22

«This answer» contains a code example showing how to convert a MethodHandle to a functional interface implementation using the same feature, Java 8’s lambda expressions and method references use.

It’s all about calling LambdaMetafactory.metafactory with the method handle, the desired interface and the name of the sole abstract method and required signature.

Both, the method’s documentation and it’s class documentation are very detailled.

So, for your request, example code may look like this:

MethodType methodType = MethodType.methodType(Integer.class, String.class);
MethodHandles.Lookup lookup = MethodHandles.lookup();
MethodHandle handle = lookup.findStatic(Integer.class, "valueOf", methodType);
Function<String,Integer> f=(Function<String,Integer>)
  LambdaMetafactory.metafactory(lookup, "apply",
    MethodType.methodType(Function.class), methodType.generic(),
    handle, methodType).getTarget().invokeExact();

System.out.println(f.apply("123"));

You have to care about the signature types here. The fourth parameter samMethodType refers the the method type of the raw interface’s functional signature, so for the raw type Function we must implement Object apply(Object) while the instantiatedMethodType describes the method Integer apply(String). That’s why the method .generic() is called on the methodType for the fourth parameter which will convert (String)Integer to (Object)Object.

This is even trickier for constructors as the constructor will be looked up with a (String)void type while the functional type is the same as in the static method case. So for a static method the method’s MethodType matches the MethodType while for a constructor we have to use a different type for the lookup:

MethodType methodType = MethodType.methodType(Integer.class, String.class);
MethodHandles.Lookup lookup = MethodHandles.lookup();
MethodHandle handle = lookup.findConstructor(
        Integer.class, MethodType.methodType(void.class, String.class));
Function<String,Integer> f=(Function<String,Integer>)
  LambdaMetafactory.metafactory(lookup, "apply",
    MethodType.methodType(Function.class), methodType.generic(),
    handle, methodType).getTarget().invokeExact();

But that’s only for completeness, for the type Integer you shouldn’t call the constructor but use valueOf method, preferably.

Adulate answered 11/12, 2014 at 10:15 Comment(1)
Thanks again! Will include that solution into the answer collection post.Boccioni
T
10

I think you'll need something like:

Function<ByteBuffer,Object> fn = (Function<ByteBuffer,Object>)
    MethodHandleProxies.asInterfaceInstance(Function.class, handle);

(Usual disclaimer: Not even compiled it. Compiled it. Seems to work.)

Tuddor answered 11/12, 2014 at 9:47 Comment(7)
It’s MethodHandleProxies, not MethodHandlesProxy and a Java 7 feature, but besides that, it should work.Adulate
Tried it. Works for me (when given a name before the =).Tuddor
@Adulate Found the typo when trying to compile. All of the MethodHandle nonsense is @Since Java SE 7.Tuddor
Works. Adding it to "function4" in the solution collection.Boccioni
@Tom Hawtin - tackline: not “all of the MethodHandle nonsense”. The LambdaMetafactory which produces more efficient interface instances and is used for lambda expressions and method references exist since Java 8. The same applies to the feature of decoding a direct handle to a MethodHandleInfo.Adulate
Regarding performance: I benchmarked various ways of executing a simple primitive mathematical operation in a loop of 500 million iterations. Inline code: 170; direct method invocation: 170; colon-colon lambda of the method: 300; LambdaMetafactory: 300; MethodHandleProxies: 8900; java.lang.reflect.Method.invoke: 11500. So LambdaMetafactory produces something as efficient as a directly declared lambda, whereas MethodHandleProxies produces something nearly as inefficient as old-school reflection.Plasmagel
@Plasmagel well, LambdaFactory in fact is used internally by lambdas, so it's expectable that it has comparable performanceOpera
B
1

Answer collection

No handle, just the lambda:

Function<String, Integer> function1 = Integer::new;
System.out.println(function1.apply("1"));

Simple (not generic, not exact) solution:

MethodType methodType = MethodType.methodType(void.class, String.class);
MethodHandle handle = MethodHandles.publicLookup().findConstructor(Integer.class, methodType);
Function<String, Integer> function2 = (s) -> {
  try {
    return (Integer) handle.invoke(s);
  } catch (Throwable t) {
    throw new Error(t);
  }
};
System.out.println(function2.apply("2"));

Using LambdaMetafactory from Holger

MethodType methodType = MethodType.methodType(Integer.class, String.class);
MethodHandles.Lookup lookup = MethodHandles.lookup();
MethodHandle handle = lookup.findConstructor(Integer.class, MethodType.methodType(void.class, String.class));
Function<String,Integer> function3 = (Function<String,Integer>) LambdaMetafactory.metafactory(lookup, "apply", MethodType.methodType(Function.class), methodType.generic(), handle, methodType).getTarget().invokeExact();
System.out.println(function3.apply("3"));

Using MethodHandleProxies from Tom Hawtin

@SuppressWarnings("unchecked")
Function<String, Integer> function4 = (Function<String, Integer>) MethodHandleProxies.asInterfaceInstance(Function.class, handle);
System.out.println(function4.apply("4"));
Boccioni answered 11/12, 2014 at 10:29 Comment(1)
Look at my example. The difference is the call .generic() on the method type for samMethodType parameter which differs from instantiatedMethodType, because Function<String, Integer> is generic, hence the raw code must implement Object apply(Object) while the actual code will perform a Integer apply(String) action…Adulate

© 2022 - 2024 — McMap. All rights reserved.