Java 8引入了对一流函数的支持,它允许将函数分配给变量.在这种情况下,变量必须是函数类型的,它由函数接口(只有一个抽象方法的接口)定义。
因此,考虑具有以下定义的接口I和类A的示例:
interface I{ int foo(); }
class A implements I{
public int foo(){return 7;}
public static int bar(){return 11;}
}我们可以将I类型的变量指定为A的实例,也可以将方法引用给A的方法bar。这两个变量都可以存储在I类型的变量上,例如:
I i1 = new A();
I i2 = A::bar;如果我们分析前一段代码的编译所产生的字节码,我们将得到:
0: new #2 // class A
3: dup
4: invokespecial #3 // Method A."<init>":()V
7: astore_1
8: invokedynamic #4, 0 // InvokeDynamic #0:foo:()LI;
13: astore_2对于i1 = new A();,很明显,对应的指令7: astore_1正在存储与I兼容的A实例。但是,作为i2 = A::bar的结果,我们正在存储8: invokedynamic #4, 0的结果。
因此,这意味着invokedynamic的结果总是目标类型的实例,而是我们用方法引用分配的变量的类型?
发布于 2015-03-24 17:53:08
每个invokedynamic字节码都引用常量池中相应的信息结构。此结构包含一个方法描述符,用于派生参数的类型和此invokedynamic指令的返回值类型。
在您的示例中,方法描述符是在源到字节码转换期间计算的()LI;。
8: invokedynamic #4, 0 // InvokeDynamic #0:foo:()LI;
^^^^^这意味着这个特定的字节码不需要参数,并且总是产生I类型的结果。
发布于 2015-03-24 17:59:11
invokedynamic指令的结果--Java 8的lambda表达式和方法引用使用它的方式--确实是目标函数interface的一个实例。
这不是JVM记住的invokedynamic指令的结果,而是引导方法返回的CallSite,在新的Java8中,LambdaMetafactory的两种方法之一都具有这种特性。
链接到CallSite指令的invokedynamic实例封装行为,而不是特定的结果值。LambdaMetafactory提供的实际行为是有意不指定的,以提供广泛的自由度,但是当前的实现显示了两种不同的行为。
对于非捕获的lambda表达式,行为是返回在invokedynamic引导过程中创建的单个实例。这可以通过创建包装在MethodHandle中的ConstantCallSite来实现。在这种情况下,invokedynamic指令的后续执行将对此实例进行计算。
对于捕获值的lambda表达式,指令将与接受捕获值的生成类的构造函数或工厂方法相链接。因此,invokedynamic指令的后续执行将表现为一个普通的对象构造(这将创建类的一个新实例,该类每次实现目标interface )。
https://stackoverflow.com/questions/29238441
复制相似问题