I try to link methods together through methodhandles, some of them are from generic types. If a function returns a generic type I have to specify Object.class for the MethodType but I see no easy way to convert it back into the generic type parameter type. In most cases it's no problem because invoke seem to convert them automatically but I must create mhs which could be run with invokeExact. Is there no easy way to cast with methodhandles?
My testcode:
public static void main(String[] args) throws Throwable {
class Prefixer {
public static String prefix(String s) {
return "Number: " + s;
}
}
IntFunction<String> converter = Integer::toString;
var lookup = MethodHandles.lookup();
var prefixMH = lookup.findStatic(Prefixer.class, "prefix", MethodType.methodType(String.class, String.class));
var converterMH = lookup.findVirtual(IntFunction.class, "apply", MethodType.methodType(Object.class, int.class));
converterMH = converterMH.bindTo(converter);
/* Doesn't work because converter is a (int)Object and no (int)String
var mh = MethodHandles.filterArguments(prefixMH, 0, converterMH);
*/
/* Does work for invoke but not for invokeExact
var prefixCasted = MethodHandles.explicitCastArguments(prefixMH, MethodType.methodType(String.class, Object.class));
var mh = MethodHandles.filterArguments(prefixCasted, 0, converterMH);
*/
/* Does work for invoke but not for invokeExact */
var mh = MethodHandles.filterArguments(prefixMH, 0, converterMH.asType(MethodType.methodType(String.class, int.class)));
System.out.println(mh.invoke(12));
System.out.println(mh.invokeExact(42));
}
Your current code looks okay to me, you just need to use a cast at the call site as well:
System.out.println((String) mh.invokeExact(42));
Otherwise the type at the call site will be (int)Object
which doesn't match the type of the MethodHandle (int)String
and you get a WMTE.
The invoke
version of the call you have:
System.out.println(mh.invoke(12));
Will implicitly convert the type of mh
into (int)Object
(the type of the call site) using an asType
call, and then invoke the resulting method handle.
If you want to do that explicitly and use invokeExact
you could do:
mh = mh.asType(MethodType.methodType(Object.class, int.class));
System.out.println(mh.invokeExact(42));