3
import java.lang.invoke.*;

public class InvokeDynamicDemo {            
    public static double doubleIt(double d){
        System.out.print("Doubling it");
        return d*2;
    }

    public static void main(String[] args) throws Throwable {    
        MethodHandles.Lookup lookUp  = MethodHandles.lookup();
        MethodHandle doubleIt = lookUp.findStatic(InvokeDynamicDemo.class, "doubleIt", MethodType.methodType(double.class,double.class));
        doubleIt.invokeExact(2.0D); // Exception 
       //doubleIt.invoke(2.0D); // No exception thrown          
    }
}

Exception in thread "main" java.lang.invoke.WrongMethodTypeException: expected (double)double but found (double)void at java.lang.invoke.Invokers.newWrongMethodTypeException(Invokers.java:340) at java.lang.invoke.Invokers.checkExactType(Invokers.java:351) at InvokeDynamicDemo.main(InvokeDynamicDemo.java:32)

What is wrong with this code , I can't figure it. Please help.

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
optional
  • 3,260
  • 5
  • 26
  • 47

2 Answers2

14

The problem is that you're not using the result of the invokeExact method. I hadn't seen this method before, but it looks like the Java compiler has to handle it in a very special way. From the MethodHandle documentation:

As is usual with virtual methods, source-level calls to invokeExact and invoke compile to an invokevirtual instruction. More unusually, the compiler must record the actual argument types, and may not perform method invocation conversions on the arguments. Instead, it must generate instructions that push them on the stack according to their own unconverted types. The method handle object itself is pushed on the stack before the arguments. The compiler then generates an invokevirtual instruction that invokes the method handle with a symbolic type descriptor which describes the argument and return types.

To issue a complete symbolic type descriptor, the compiler must also determine the return type. This is based on a cast on the method invocation expression, if there is one, or else Object if the invocation is an expression, or else void if the invocation is a statement. The cast may be to a primitive type (but not void).

At the moment you're calling the method without using the result, so the compiler infers that you expect it to be a void method - hence the (double)void part of the exception.

If you change the call to:

double result = (double) doubleIt.invokeExact(2.0);

... then the compiler knows what return type you're expecting, and can create the appropriate symbolic type descriptor.

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • This is due to invokeExact generic result type(Object) .Hence can't create exact match if I do not specify result type ? – optional Dec 16 '17 at 07:53
  • 1
    @optional: Sort of - basically the compiler has to spot how you use the result to indicate which result type you expect. (If that had been the only thing it needed to do with the call, it might have been better to have it as a separate parameter - but as you can see, it needs to handle the arguments specially too.) – Jon Skeet Dec 16 '17 at 07:56
  • @optional: Looks like I didn't include the most important part of the doc when copying, either. I've done that now, which will hopefully make it much clearer :) – Jon Skeet Dec 16 '17 at 07:59
0

From Oracle Documentation, it says that invokeExact method Invokes the method handle, allowing any caller type descriptor, but requiring an exact type match. The symbolic type descriptor at the call site of invokeExact must exactly match this method handle's type. No conversions are allowed on arguments or return values.

But, invoke method Invokes the method handle, allowing any caller type descriptor, and optionally performing conversions on arguments and return values.

Arvind Katte
  • 995
  • 2
  • 10
  • 20
  • On its own, I don't see how this explains it - you haven't said what's wrong in the example. – Jon Skeet Dec 16 '17 at 07:57
  • in the `invokeExact` method, he is trying to return double value, but the compiler thinks it's return value is void, because this method will not allow any automatic conversion return type. after converting the return type to double, like `double d = (double)doubleIt.invokeExact(2.0d); // Exception`. it will resolve – Arvind Katte Dec 16 '17 at 08:02
  • Yes, that's as per my answer - but none of what you've written in the comment is *at all* obvious from your answer. – Jon Skeet Dec 16 '17 at 08:03