Search code examples
javainvokedynamicmethodhandle

MethodType transformation for MethodHandle to accept Array Object paramters


I want to adapt String.startsWith from (String, String)boolean to (String[])boolean, so that it can accepts String[] parameters, in which first two parameters will be mapped to the (String, String). Therefore, I wrote below sample code:

MethodHandle test =MethodHandles.publicLookup().findVirtual(String.class, "startsWith", MethodType.methodType(boolean.class, String.class));
String[] myArgs = {"result", "data", "sijie"};
MethodHandle adapt = test.asSpreader(String[].class, 2);
System.out.println("Adapt... "+ adapt.type().toString());
System.out.println("Compare Result: "+ adapt.invokeExact(myArgs)); //Expect to return false. 

The MethodHandle to String.startsWith is adapted to the adapt boolean (String[]) at first. But the result shows the adapt.invokeExact failure.

Adapt... (String[])boolean
Exception in thread "main" java.lang.invoke.WrongMethodTypeException: expected (String[])boolean but found (String[])Object
    at java.lang.invoke.Invokers.newWrongMethodTypeException(Invokers.java:349)
    at java.lang.invoke.Invokers.checkExactType(Invokers.java:360)
    at org.bytecode.generation.sample.App.main(App.java:78)

The new (String[]) Object in the stacktrace is quite confusing. Can anyone provide some suggestions on how to fix it?

Thanks

This problem can be abstract as: How to adapt a Methodhandle that only accepts (String, String)boolean so that it can accepts (String[])boolean parameters?


Solution

  • The problem is with the invokeExact call -- because invokeExact is signature polymorphic, you need to specify the return type explicitly using a cast:

    System.out.println("Compare Result: "+ (boolean)adapt.invokeExact(myArgs));
    

    That's what the stack trace is trying to tell you with expected (String[])boolean but found (String[])Object -- because you didn't have the cast to boolean, javac assumed the method handle would have type (String[])Object, but at runtime, it had type (String[])boolean instead. (The order might be confusing. Think of it like "given the method handle, what type do I expect to have been recorded at compile time for invokeExact?")

    See also my answer to Why can't I .invokeExact() here, even though the MethodType is OK?. (That question involved subtyping and the resolution involved asType as well as adding a cast, but the information is similar.)