Convert MethodHandle to method reference (here Function)

十年热恋 提交于 2019-11-30 18:03:37
Holger

«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.

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.)

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"));
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!