Java 8引入了对一流函数的支持,该函数允许将函数分配给变量。在这种情况下,变量必须是函数类型,它是由函数接口(interface)(仅一种抽象方法的接口(interface))定义的。

因此,考虑一个具有以下定义的接口(interface)IA类的示例:

interface I{ int foo(); }
class A implements I{
  public int foo(){return 7;}
  public static int bar(){return 11;}
}

我们可以将I的实例分配给A类型的变量,或者将的方法引用分配给bar的方法。两者都可以存储在A类型的变量上,例如:
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

对于I来说,显然是相应的指令i1 = new A();正在存储与7: astore_1兼容的A实例。但是,作为I的结果,我们将存储i2 = A::bar的结果。

因此,这意味着8: invokedynamic #4, 0的结果始终是目标类型的实例,这是我们通过方法引用分配的变量的类型?

最佳答案

每个invokedynamic字节码都引用常量池中的对应CONSTANT_InvokeDynamic_info结构。该结构包含一个Method Descriptor,它用于派生此invokedynamic指令的参数类型和返回值的类型。

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

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

这意味着该特定字节码不包含任何参数,并且始终产生I类型的结果。

10-06 13:48