MethodHandle cast return type

422 Views Asked by At

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));
}
1

There are 1 best solutions below

0
On BEST ANSWER

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