Java 8 Lambda表达式编译成什么?

26
考虑以下Java 8代码片段。
public static void main(String[] args) {            
   List<Integer> integers = Arrays.asList(1, 2, 3, 4, 5);

   Consumer<Integer>  consumer = x -> System.out.print(x);  

   integers.forEach(consumer);
}

“Consumer<Integer> consumer = x -> System.out.print(x)”被编译成了什么?

我知道Lambda表达式并不是作为匿名内部类来实现的。但是,Consumer<Integer>是一个接口,因此x -> System.out.print(x)必须产生某种类型的对象,但不清楚产生了哪种类型的对象。

在Java 8中是否有一种新类型的对象来表示lambda表达式?

更新:这是反编译程序,该程序使用eclipse java 8编译器进行编译,下面的输出来自打开类文件时的eclipse。

看起来,lambda表达式正在转换为包含lambda表达式的类上的静态方法private static synthetic void lambda$0(java.lang.Integer x);

// Compiled from Example.java (version 1.8 : 52.0, super bit)
public class Example {

  // Method descriptor #6 ()V
  // Stack: 1, Locals: 1
  public Example();
    0  aload_0 [this]
    1  invokespecial java.lang.Object() [8]
    4  return
      Line numbers:
        [pc: 0, line: 7]
      Local variable table:
        [pc: 0, pc: 5] local: this index: 0 type: Example

  // Method descriptor #15 ([Ljava/lang/String;)V
  // Stack: 4, Locals: 3
  public static void main(java.lang.String[] args);
     0  iconst_5
     1  anewarray java.lang.Integer [16]
     4  dup
     5  iconst_0
     6  iconst_1
     7  invokestatic java.lang.Integer.valueOf(int) : java.lang.Integer [18]
    10  aastore
    11  dup
    12  iconst_1
    13  iconst_2
    14  invokestatic java.lang.Integer.valueOf(int) : java.lang.Integer [18]
    17  aastore
    18  dup
    19  iconst_2
    20  iconst_3
    21  invokestatic java.lang.Integer.valueOf(int) : java.lang.Integer [18]
    24  aastore
    25  dup
    26  iconst_3
    27  iconst_4
    28  invokestatic java.lang.Integer.valueOf(int) : java.lang.Integer [18]
    31  aastore
    32  dup
    33  iconst_4
    34  iconst_5
    35  invokestatic java.lang.Integer.valueOf(int) : java.lang.Integer [18]
    38  aastore
    39  invokestatic java.util.Arrays.asList(java.lang.Object[]) : java.util.List [22]
    42  astore_1 [integers]
    43  invokedynamic 0 accept() : java.util.function.Consumer [31]
    48  astore_2 [consumer]
    49  getstatic java.lang.System.out : java.io.PrintStream [32]
    52  aload_2 [consumer]
    53  invokevirtual java.lang.Object.getClass() : java.lang.Class [38]
    56  invokevirtual java.lang.Class.getCanonicalName() : java.lang.String [42]
    59  invokevirtual java.io.PrintStream.println(java.lang.String) : void [48]
    62  getstatic java.lang.System.out : java.io.PrintStream [32]
    65  aload_2 [consumer]
    66  invokevirtual java.lang.Object.getClass() : java.lang.Class [38]
    69  invokevirtual java.lang.Class.getTypeName() : java.lang.String [54]
    72  invokevirtual java.io.PrintStream.println(java.lang.String) : void [48]
    75  aload_1 [integers]
    76  aload_2 [consumer]
    77  invokeinterface java.util.List.forEach(java.util.function.Consumer) : void [57] [nargs: 2]
    82  return
      Line numbers:
        [pc: 0, line: 10]
        [pc: 43, line: 12]
        [pc: 49, line: 14]
        [pc: 62, line: 15]
        [pc: 75, line: 17]
        [pc: 82, line: 18]
      Local variable table:
        [pc: 0, pc: 83] local: args index: 0 type: java.lang.String[]
        [pc: 43, pc: 83] local: integers index: 1 type: java.util.List
        [pc: 49, pc: 83] local: consumer index: 2 type: java.util.function.Consumer
      Local variable type table:
        [pc: 43, pc: 83] local: integers index: 1 type: java.util.List<java.lang.Integer>
        [pc: 49, pc: 83] local: consumer index: 2 type: java.util.function.Consumer<java.lang.Integer>

  // Method descriptor #73 (Ljava/lang/Integer;)V
  // Stack: 2, Locals: 1
  private static synthetic void lambda$0(java.lang.Integer x);
    0  getstatic java.lang.System.out : java.io.PrintStream [32]
    3  aload_0 [x]
    4  invokevirtual java.io.PrintStream.print(java.lang.Object) : void [74]
    7  return
      Line numbers:
        [pc: 0, line: 12]
      Local variable table:
        [pc: 0, pc: 8] local: x index: 0 type: java.lang.Integer

  Inner classes:
    [inner class info: #96 java/lang/invoke/MethodHandles$Lookup, outer class info: #98 java/lang/invoke/MethodHandles
     inner name: #100 Lookup, accessflags: 25 public static final]
Bootstrap methods:
  0 : # 89 arguments: {#90,#93,#94}
}

1
尝试使用consumer.getClass()来获取一些提示,然后阅读这个和JLS。 - Sotirios Delimanolis
1
@SotiriosDelimanolis consumer.getClass() 的输出为 Example$$Lambda$1/1072591677。 - ams
使用 javap -c YourClass.class 命令来查看代码的编译情况。 - Luiggi Mendoza
我当前的环境没有Java 8,你能否运行javap -v -c ..命令,这样我们就可以获取常量了。 invokedynamic 0 accept()应该是获取实例的内容,但是0引用了常量池中的某些内容。 - Sotirios Delimanolis
1个回答

15

Java 8语言规范的最新草案指出(第15.27.4章):

lambda表达式的值是对具有以下属性的类实例的引用:

  • 该类实现了目标函数式接口,并且如果目标类型是交叉类型,则实现交叉类型的每个其他接口元素。
  • 该类声明一个方法,该方法覆盖函数式接口超类型的抽象方法(以及其超接口的一些其他方法)。
  • 方法的参数类型、返回类型和抛出类型由接口的函数类型给出。
  • 如果lambda主体是一个表达式,则该方法的主体具有计算lambda主体的效果;如果是一个块,则具有执行lambda主体的效果。如果需要结果,则从方法返回结果。
  • 该类不覆盖上述接口或接口的其他方法,但它可以覆盖Object类的方法。

请注意,JLS没有关于代码应如何编译的规定,只是要求字节码支持上述规范。

因此,lambda表达式返回的对象

x -> System.out.print(x);  

将成为遵循上述规则的类的一个实例。

鉴于您的评论,

consumer.getClass()

返回以下类

Example$$Lambda$1/1072591677

看起来它正在生成特定于lambda表达式的类似代理的类。

请参见这里:


请注意,在Java 8版本的实现中,Lambda表达式不会编译成单独的类文件。 - Victor Grazi
@victor 对,这些都是在运行时生成的类。 - Sotirios Delimanolis
@Trejkaz 这似乎不对。你能发一个例子吗? - Sotirios Delimanolis
是的,我还没有关于它的问题,只是与其他评论相关的观察。 - Hakanai
2
Java编译器将为既不明确声明也不隐式声明的代码构造生成“合成”方法。我们知道,Lambda表达式/函数是函数接口中抽象方法的匿名类方法实现,如果我们查看带有Lambda表达式的已编译类文件的字节码,它使用新的INVOKEDYNAMIC指令将此调用站点动态链接到**实际Lambda函数,该函数转换为private static synthetic lambda$0(Ljava/lang/String;)**,而不是创建一个将包装Lambda函数的新对象。 - Rams
显示剩余3条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接