2

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或方法的方法引用barA两者都可以存储在 type 的变量中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正在存储一个AI. 但是,由于i2 = A::bar我们正在存储8: invokedynamic #4, 0.

那么,这意味着 an 的结果invokedynamic始终是目标类型的实例,即我们使用方法引用分配的变量的类型?

4

2 回答 2

6

每个invokedynamic字节码引用常量池中对应的CONSTANT_InvokeDynamic_info结构。该结构包含一个方法描述符,用于派生该invokedynamic指令的参数类型和返回值类型。

在您的示例中,方法描述符是()LI;在源到字节码的转换期间计算的。

8: invokedynamic #4,  0              // InvokeDynamic #0:foo:()LI;
                                                             ^^^^^

这意味着这个特定的字节码不需要任何参数并且总是产生 type 的结果I

于 2015-03-24T17:53:08.897 回答
4

指令的结果,invokedynamicJava 8 的 lambda 表达式和方法引用使用它的方式,确实是目标函数的实例interface

invokedynamicJVM 记住的不是指令的结果,而是CallSite引导方法返回的结果,如果新的 Java 8 具有LambdaMetafactory.

CallSite链接到指令的实例invokedynamic封装了行为,而不是特定的结果值。故意未指定提供的实际行为LambdaMetafactory以提供广泛的自由度,但当前的实现表现出两种不同的行为。

对于非捕获 lambda 表达式,行为将返回在invokedynamic引导期间创建的单个实例。这可以通过创建一个包裹MethodHandleConstantCallSite. 在这种情况下,指令的后续执行invokedynamic将评估此实例。

对于捕获值的 lambda 表达式,指令将链接到接受捕获值的生成类的构造函数或工厂方法。因此,invokedynamic指令的后续执行将表现得像一个普通的对象构造(它创建一个interface每次实现目标的类的新实例)。

于 2015-03-24T17:59:11.970 回答