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(); // ???
Community
  • 1
  • 1
Sormuras
  • 8,491
  • 1
  • 38
  • 64

3 Answers3

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.

Community
  • 1
  • 1
Holger
  • 285,553
  • 42
  • 434
  • 765
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.)

Tom Hawtin - tackline
  • 145,806
  • 30
  • 211
  • 305
  • 2
    It’s `MethodHandleProxies`, not `MethodHandlesProxy` and a Java 7 feature, but besides that, it should work. – Holger Dec 11 '14 at 10:30
  • Tried it. Works for me (when given a name before the `=`). – Tom Hawtin - tackline Dec 11 '14 at 10:30
  • @Holger Found the typo when trying to compile. All of the `MethodHandle` nonsense is @Since Java SE 7. – Tom Hawtin - tackline Dec 11 '14 at 10:40
  • Works. Adding it to "function4" in the solution collection. – Sormuras Dec 11 '14 at 10:46
  • 1
    @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`. – Holger Dec 11 '14 at 10:51
  • 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. – ctrueden Jul 02 '19 at 21:11
  • @ctrueden well, LambdaFactory in fact is used internally by lambdas, so it's expectable that it has comparable performance – kb1000 Aug 23 '19 at 05:35
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"));
Sormuras
  • 8,491
  • 1
  • 38
  • 64
  • 2
    Look at my example. The difference is the call `.generic()` on the method type for `samMethodType` parameter which differs from `instantiatedMethodType`, because `Function` is generic, hence the raw code must implement `Object apply(Object)` while the actual code will perform a `Integer apply(String)` action… – Holger Dec 11 '14 at 10:33