MethodHandle invokeExact 带有 return 和参数的静态方法

MethodHandle invokeExact a static method with return and parameter

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)

这段代码有什么问题,我想不通。请帮忙。

问题是您没有使用 invokeExact 方法的结果。我以前没见过这种方法,但看起来 Java 编译器必须以 非常 特殊的方式处理它。来自 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).

目前您正在调用该方法而不使用结果,因此编译器推断您希望它是一个 void 方法 - 因此是异常的 (double)void 部分。

如果您将调用更改为:

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

... 然后编译器知道您期望的 return 类型,并可以创建适当的符号类型描述符。

来自Oracle Documentation,它说invokeExact方法调用方法句柄,允许任何调用者类型描述符,但需要精确的类型匹配。 invokeExact 调用点的符号类型描述符必须与该方法句柄的类型完全匹配。 参数或 return 值不允许转换。

但是,invoke 方法调用方法句柄,允许任何调用者类型描述符,并且 可选择对参数和 return 值执行转换。