0%

Java8 Lambda表达式详解

Lambda 表达式详解

1. Lambda 表达式的形式

  1. 无参数

    1
    2
    3
    4
    5
    6
    7
    NoArgument noArgument = () -> {
    System.out.println("No Argument");
    };
    // 定义
    public interface NoArgument {
    void noArg();
    }
  2. 一个参数

    1
    2
    3
    4
    5
    6
    7
    OneArgument oneArgument = (a) -> {
    System.out.println("One Argument:" + a);
    };
    // 定义
    public interface OneArgument {
    void oneArg(int one);
    }
  1. 多个参数

    1
    2
    3
    4
    5
    6
    7
    8
    ManyArguments manyArguments = (a, b, many) -> {
    System.out.printf("ManyArguments:" + a + "," + b + "," + Arrays.toString(many));
    };

    // 定义
    public interface ManyArguments {
    void manyArg(int one, int two, int... other);
    }
  2. 当lambda中的表达式只有一行时可缩写:

    1
    NoArgument noArgument = () -> System.out.println("No Argument");

2. Java 8中的函数接口

java8中已经定义了很多lambda函数操作,这些是lambda表达式操作的基础,如:

BiFunction BinaryOperator Function Predicate Supplier 等等,下面先简要介绍BiFunction BinaryOperator Function 的用法:

  • BinaryOperator

    1
    BinaryOperator<Float> addOperator = (a, b) -> a + b;

    BinaryOperator<T> 是二元操作,将2个为T类型的参数进行操作,返回同类型的结果T

    1
    Float result = addOperator.apply(1.f, 2.f);

    apply表示将参数1.f 2.f 输入,返回相加的结果result

  • BiFunction

    1
    BiFunction<Float, Integer, Float> subOperator = (Float a, Integer b) -> a - b;

    BiFunction<T, U, R>二元操作,其中TU是输入参数类型,R输出参数类型

    上式表达的意思是,将Float aInteger b相减,返回Float类型结果。

    查看BiFunction源码,我们可以看出:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    @FunctionalInterface
    public interface BiFunction<T, U, R> {

    R apply(T t, U u);

    default <V> BiFunction<T, U, V> andThen(Function<? super R, ? extends V> after) {
    Objects.requireNonNull(after);
    return (T t, U u) -> after.apply(apply(t, u));
    }
    }

    @FunctionalInterface定义函数式接口

    apply接口方法,andThen 采用default修饰的接口方法,有默认实现,其中default是java 8新增的。

    在 java 8 之前,接口与其实现类之间的 耦合度 太高了(tightly coupled),当需要为一个接口添加方法时,所有的实现类都必须随之修改。默认方法解决了这个问题,它可以为接口添加新的方法,而不会破坏已有的接口的实现。这在 lambda 表达式作为 java 8 语言的重要特性而出现之际,为升级旧接口且保持向后兼容(backward compatibility)提供了途径。

    andThen 默认实现表达的意思是,在BiFunction 执行apply方法后,返回结果通过Function来处理。

    1
    2
    3
    BiFunction<Float, Integer, Float> mulOperator = (Float a, Integer b) -> a * b;
    mulOperator.andThen((a) -> a * a);
    Float result = mulOperator.apply(1.f, 4);
  • Function

    1
    Function<Float, Integer> trsOperator = (a) -> a.intValue();

    Function<T, R>一元操作,输入T类型,返回R类型

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    @FunctionalInterface
    public interface Function<T, R> {

    R apply(T t);

    default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {
    Objects.requireNonNull(before);
    return (V v) -> apply(before.apply(v));
    }

    default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {
    Objects.requireNonNull(after);
    return (T t) -> after.apply(apply(t));
    }

    static <T> Function<T, T> identity() {
    return t -> t;
    }
    }

2. 自定义函数接口

1
2
3
4
5
import java.lang.FunctionalInterface;
@FunctionalInterface
public interface TestFun<T, R> {
R test(T t);
}
1
TestFun<Integer, Integer> tf = (a) -> a * a;

3. 函数接口与普通接口的区别

看到这里或许有人会问,函数接口与普通接口都可以写成lambda形式,那他们之前还有什么区别呢?

使用@FunctionalInterface 时,在编译过程中,会强制检javac查这个接口是不是符合函数式接口的标准。如果不符合函数接口标准会报错。

3.1 接口函数标准

  • 注解的是接口类型,而不是枚举、类或其他注解
  • 注解的接口有且只有一个非默认方法(可包含多个方法,但是其他的方法必须有默认实现,即default实现,但必须有一个且只有一个没有显示的方法)

除了定义有@FunctionalInterface 的接口,编译器会处理符合的任何接口,将其定义为函数式接口,无论是否有@FunctionalInterface 注解。二者有交集,但不完全一样。

3.2 示例

  1. 当有 @FunctionalInterface 修饰的接口中包含多个方法时:

  2. 当有 @FunctionalInterface 修饰的接口中,没有必须实现的方法时,即全部都有默认实现:

  3. 普通类可实现有 @FunctionalInterface 修饰的接口

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    import java.util.Objects;
    public class Age implements Comparable<Age>, TestFun {
    int age;
    public Age(int age) {
    this.age = age;
    }
    @Override
    public int compareTo(Age o) {
    Objects.requireNonNull(o);
    return this.age - o.age;
    }
    @Override
    public Object test(Object o) {
    return age;
    }
    }
  4. 对于只有一个方法的Comparable 接口,也可写为函数式。下面例子只是用来说明这个情况。

    1
    2
    3
    Comparable comparable= (Object o) ->{return o.hashCode();};
    Comparable comparable= (o) ->{return o.hashCode();};
    Comparable comparable= Object::hashCode;

    但是实际应用 Comparable 接口时,会像类 Age 一样应用,Comparable 接口的方法会与外部相关连。Comparable 接口的意义是比较,自然在该类的实例之间存在某种顺序。因此,就这个接口表达的意义上来说,这个接口并不是函数式接口。

4. Lambda表达式调用过程

lambda表达式在调用过程中有以下特点:

  1. lambda表达式引用的是值,而不是变量。
  2. lambda表达式的类型依赖与上下文,经由编译器推断得到具体类型。
  3. lambda表达式可用来简写匿名内部类,但是lambda表达式不等同与匿名内部类,JVM内部是通过invokedynamic指令来实现Lambda表达式的。

4.1 Lambda表达式调用分析

下面,我们跟随包含lambda表达式的class文件,来一步步解析其调用过程:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
import java.io.Serializable;
public class TestLambda {
public static void main(String[] args) {
TestFun<Integer, Integer> tf = (a) -> a * a;
System.out.println(tf.test(10));
new TestLambda().test(new OneArgument() {
@Override
public void oneArg(int one) {
System.out.println(one);
}
});
new TestLambda().test(System.out::println);
new TestLambda().testSeri(() -> {
System.out.println("Serializable");
});
}
void test(OneArgument oneArgument) {
oneArgument.oneArg(100);
}
void testSeri(NoArgument noArgument) {
noArgument.noArg();
}
public interface OneArgument {
void oneArg(int one);
}
@FunctionalInterface
public interface NoArgument extends Serializable {
void noArg();
}
@FunctionalInterface
public interface TestFun<T, R> {
R test(T t);
}
}
1
2
3
4
5
import java.lang.FunctionalInterface;
@FunctionalInterface
public interface TestFun<T, R> {
R test(T t);
}
1
javap -verbose TestLambda.class

对于类TestLambda ,查看其生存的class文件关键内容如下(全部内容见文章未):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
Code:
stack=3, locals=2, args_size=1
0: invokedynamic #2, 0 // InvokeDynamic #0:test:()Lcn/cwiki/java8/TestLambda$TestFun;
5: astore_1
6: getstatic #3 // Field java/lang/System.out:Ljava/io/PrintStream;
9: aload_1
10: bipush 10
12: invokestatic #4 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
15: invokeinterface #5, 2 // InterfaceMethod cn/cwiki/java8/TestLambda$TestFun.test:(Ljava/lang/Object;)Ljava/lang/Object;
20: invokevirtual #6 // Method java/io/PrintStream.println:(Ljava/lang/Object;)V
23: new #7 // class cn/cwiki/java8/TestLambda
26: dup
27: invokespecial #8 // Method "<init>":()V
30: new #9 // class cn/cwiki/java8/TestLambda$1
33: dup
34: invokespecial #10 // Method cn/cwiki/java8/TestLambda$1."<init>":()V
37: invokevirtual #11 // Method test:(Lcn/cwiki/java8/TestLambda$OneArgument;)V
40: new #7 // class cn/cwiki/java8/TestLambda
43: dup
44: invokespecial #8 // Method "<init>":()V
47: getstatic #3 // Field java/lang/System.out:Ljava/io/PrintStream;
50: dup
51: invokevirtual #12 // Method java/lang/Object.getClass:()Ljava/lang/Class;
54: pop
55: invokedynamic #13, 0 // InvokeDynamic #1:oneArg:(Ljava/io/PrintStream;)Lcn/cwiki/java8/TestLambda$OneArgument;
60: invokevirtual #11 // Method test:(Lcn/cwiki/java8/TestLambda$OneArgument;)V
63: new #7 // class cn/cwiki/java8/TestLambda
66: dup
67: invokespecial #8 // Method "<init>":()V
70: invokedynamic #14, 0 // InvokeDynamic #2:noArg:()Lcn/cwiki/java8/TestLambda$NoArgument;
75: invokevirtual #15 // Method testSeri:(Lcn/cwiki/java8/TestLambda$NoArgument;)V
78: return

解读class中code区内容:

1
2
TestFun<Integer, Integer> tf = (a) -> a * a;
System.out.println(tf.test(10));

0: invokedynamic #2, 0 调用动态方法,即lambda表达式,invokedynami指令出现的地方都称为一个动态调用点

5: astore_1 将reference类型保存到本地变量中,此处包含的即是lambda表达式的调用点CallSite

6: getstatic #3 获取类的静态字段值,#3 在常量池中对于public final static PrintStream out 即获取System类中的静态变量out

9: aload_1 从局部变量中加在一个reference类型到操作数中,即TestFun<Integer, Integer> tf = (a) -> a * a 的调用点

10: bipush 10 将byte类型数据入栈,即将10入栈

12: invokestatic #4 调用静态方法,此处调用的是Integer.valueOf 将10装箱

5: invokeinterface #5, 2 调用接口方法,此处即TestFun.test

20: invokevirtual #6 调用实例方法,即PrintStream.println

1
2
3
4
5
6
new TestLambda().test(new OneArgument() {
@Override
public void oneArg(int one) {
System.out.println(one);
}
});

23: new #7 创建一个对象,此处即 TestLambda

26: dup 复制操作数栈栈顶的值,并插入到操作数栈栈顶,此处的值为TestLambda

27: invokespecial #8 调用实例方法,专门用来调用父类方法、私有方法和实例初始化方法,此时调用的是<init><init> 是jvm里类的实例方法,此处即调用 TestLambda 的构造方法

30: new #9 33: dup 34: invokespecial #10 三条指令则是新建 TestLambda$1 类,并初始化,即匿名内部类OneArgument

37: invokevirtual #11 执行TestLambda.test#11 对于常量池中TestLambda.test方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
void test(cn.cwiki.java8.TestLambda$OneArgument);
descriptor: (Lcn/cwiki/java8/TestLambda$OneArgument;)V
flags:
Code:
stack=2, locals=2, args_size=2
0: aload_1
1: bipush 100
3: invokeinterface #16, 2 // InterfaceMethod cn/cwiki/java8/TestLambda$OneArgument.oneArg:(I)V
8: return
LineNumberTable:
line 22: 0
line 23: 8
LocalVariableTable:
Start Length Slot Name Signature
0 9 0 this Lcn/cwiki/java8/TestLambda;
0 9 1 oneArgument Lcn/cwiki/java8/TestLambda$OneArgument;

查看test方法的指令:

0: aload_1 从局部变量中加在一个reference类型到操作数中,此处加载的OneArgument

1: bipush 100 将byte类型数据入栈,即将100入操作数栈

3: invokeinterface #16, 2 调用接口方法OneArgument.oneArg

8: return 返回

1
new TestLambda().test(System.out::println);

40: new #7 43: dup 44: invokespecial #8 新建TestLambda 并初始化

47: getstatic #3 获取PrintStream

50: dup 复制操作数栈栈顶的值,并插入到操作数栈栈顶,此处是 PrintStream

51: invokevirtual #12 引用方法System.out::println

54: pop 将操作数栈的栈顶元素出栈,即 PrintStream

55: invokedynamic #13, 0 调用动态方法,即调用lambda表达式

60: invokevirtual #11 调用引用方法

1
2
3
new TestLambda().testSeri(() -> {
System.out.println("Serializable");
});

63: new #7 66: dup 67: invokespecial #8 70: invokedynamic #14, 0 75: invokevirtual #15 新建 TestLambda 类,初始化,并调用lambda表达式,返回

从分析中可以看出,在lambda表达式处都是调用invokedynamic 指令:

1
2
3
0: invokedynamic #2,  0
55: invokedynamic #13, 0
70: invokedynamic #14, 0

而在匿名内不类处,则是调用 new 指令:

1
30: new           #9 

虽然在写法上,lambda可用来简写匿名内部类,但是两者在JVM指令上,却有很大不用。并且在匿名内部类的编译过程中会显式生成内部类。

下面我们来看一下 invokedynamic 指令做的事情。

4.2 invokedynamic 指令

invokedynamic 指令用于调用以绑定了 invokedynamic 指令的调用点对象(call site object)并作为目标方法。 调用点对象是一个特殊的语法结构,当一条 invokedynamic 指令首次被Java虚拟机执行前,Java虚拟机将会执行一个引导方法(bootstrap method)并以这个方法的运行结果作为调用点对象。因此,每条 invokedynamic 指令都有独一无二的链接状态,这是它与其它方法调用指令的一个差异。

代码中,每条 invokedynamic 指令出现的位置都称为一个 动态调用点

1
2
3
0: invokedynamic #2,  0
55: invokedynamic #13, 0
70: invokedynamic #14, 0

invokedynamic 指令后面的无符号数#2,0 #13,0 #14, 0 用于构建一个指向当前类运行时常量池的索引值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
BootstrapMethods:
0: #85 invokestatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
Method arguments:
#86 (Ljava/lang/Object;)Ljava/lang/Object;
#87 invokestatic cn/cwiki/java8/TestLambda.lambda$main$0:(Ljava/lang/Integer;)Ljava/lang/Integer;
#88 (Ljava/lang/Integer;)Ljava/lang/Integer;
1: #85 invokestatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
Method arguments:
#101 (I)V
#102 invokevirtual java/io/PrintStream.println:(I)V
#101 (I)V
2: #104 invokestatic java/lang/invoke/LambdaMetafactory.altMetafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;[Ljava/lang/Object;)Ljava/lang/invoke/CallSite;
Method arguments:
#105 ()V
#106 invokestatic cn/cwiki/java8/TestLambda.lambda$main$22abaf9d$1:()V
#105 ()V
#107 5
#108 0

在class文件未,生成的对应的BootstrapMethods方法如上。

可以看到BootstrapMethods方法中,通过调用静态方法LambdaMetafactory.metafactory 来生成CallSiteCallSite可以被视为函数对象的”工厂”,用于创建合适的功能对象。当接口继承了Serializable 接口时,会调用LambdaMetafactory.altMetafactory 后面会介绍二者的区别。

查看LambdaMetafactory.metafactory源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public static CallSite metafactory(MethodHandles.Lookup caller,
String invokedName,
MethodType invokedType,
MethodType samMethodType,
MethodHandle implMethod,
MethodType instantiatedMethodType)
throws LambdaConversionException {
AbstractValidatingLambdaMetafactory mf;
mf = new InnerClassLambdaMetafactory(caller, invokedType,
invokedName, samMethodType,
implMethod, instantiatedMethodType,
false, EMPTY_CLASS_ARRAY, EMPTY_MT_ARRAY);
mf.validateMetafactoryArgs();
return mf.buildCallSite();
}

MethodHandles.Lookup caller 用于确定动态调用点发生在哪个类中,由JVM提供

String invokedName 要实现的方法的名称(test),即InvokeDynamic 对应NameAndType,由JVM提供

MethodType invokedType CallSite签名描述,包含有CallSite方法的参数、返回类型等信息,由JVM提供

MethodType samMethodType 实现的方法的描述,包含实现的方法的参数、返回类型等信息

MethodHandle implMethod implMethod 内提供了对应方法的实现

MethodType instantiatedMethodType 实例化方法的描述,instantiatedMethodType 的参数类型是samMethodType 的子类型或与之相同,instantiatedMethodType 返回值类型是samMethodType 的子类型或同类型,允许强制转换。

我们来看一下buildCallSite 方法的实现过程:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
@Override
CallSite buildCallSite() throws LambdaConversionException {
final Class<?> innerClass = spinInnerClass();
if (invokedType.parameterCount() == 0) {
final Constructor<?>[] ctrs = AccessController.doPrivileged(
new PrivilegedAction<Constructor<?>[]>() {
@Override
public Constructor<?>[] run() {
Constructor<?>[] ctrs = innerClass.getDeclaredConstructors();
if (ctrs.length == 1) {
// The lambda implementing inner class constructor is private, set
// it accessible (by us) before creating the constant sole instance
ctrs[0].setAccessible(true);
}
return ctrs;
}
});
if (ctrs.length != 1) {
throw new LambdaConversionException("Expected one lambda constructor for "
+ innerClass.getCanonicalName() + ", got " + ctrs.length);
}

try {
Object inst = ctrs[0].newInstance();
return new ConstantCallSite(MethodHandles.constant(samBase, inst));
}
catch (ReflectiveOperationException e) {
throw new LambdaConversionException("Exception instantiating lambda object", e);
}
} else {
try {
UNSAFE.ensureClassInitialized(innerClass);
return new ConstantCallSite(
MethodHandles.Lookup.IMPL_LOOKUP
.findStatic(innerClass, NAME_FACTORY, invokedType));
}
catch (ReflectiveOperationException e) {
throw new LambdaConversionException("Exception finding constructor", e);
}
}
}

buildCallSite第一步,调用了spinInnerClass

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
private Class<?> spinInnerClass() throws LambdaConversionException {
String[] interfaces;
String samIntf = samBase.getName().replace('.', '/');
boolean accidentallySerializable = !isSerializable && Serializable.class.isAssignableFrom(samBase);
if (markerInterfaces.length == 0) {
interfaces = new String[]{samIntf};
} else {
// Assure no duplicate interfaces (ClassFormatError)
Set<String> itfs = new LinkedHashSet<>(markerInterfaces.length + 1);
itfs.add(samIntf);
for (Class<?> markerInterface : markerInterfaces) {
itfs.add(markerInterface.getName().replace('.', '/'));
accidentallySerializable |= !isSerializable && Serializable.class.isAssignableFrom(markerInterface);
}
interfaces = itfs.toArray(new String[itfs.size()]);
}

cw.visit(CLASSFILE_VERSION, ACC_SUPER + ACC_FINAL + ACC_SYNTHETIC,
lambdaClassName, null,
JAVA_LANG_OBJECT, interfaces);

// Generate final fields to be filled in by constructor
for (int i = 0; i < argDescs.length; i++) {
FieldVisitor fv = cw.visitField(ACC_PRIVATE + ACC_FINAL,
argNames[i],
argDescs[i],
null, null);
fv.visitEnd();
}

generateConstructor();

if (invokedType.parameterCount() != 0) {
generateFactory();
}

// Forward the SAM method
MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, samMethodName,
samMethodType.toMethodDescriptorString(), null, null);
mv.visitAnnotation("Ljava/lang/invoke/LambdaForm$Hidden;", true);
new ForwardingMethodGenerator(mv).generate(samMethodType);

// Forward the bridges
if (additionalBridges != null) {
for (MethodType mt : additionalBridges) {
mv = cw.visitMethod(ACC_PUBLIC|ACC_BRIDGE, samMethodName,
mt.toMethodDescriptorString(), null, null);
mv.visitAnnotation("Ljava/lang/invoke/LambdaForm$Hidden;", true);
new ForwardingMethodGenerator(mv).generate(mt);
}
}

if (isSerializable)
generateSerializationFriendlyMethods();
else if (accidentallySerializable)
generateSerializationHostileMethods();

cw.visitEnd();

// Define the generated class in this VM.

final byte[] classBytes = cw.toByteArray();

// If requested, dump out to a file for debugging purposes
if (dumper != null) {
AccessController.doPrivileged(new PrivilegedAction<Void>() {
@Override
public Void run() {
dumper.dumpClass(lambdaClassName, classBytes);
return null;
}
}, null,
new FilePermission("<<ALL FILES>>", "read, write"),
// createDirectories may need it
new PropertyPermission("user.dir", "read"));
}

return UNSAFE.defineAnonymousClass(targetClass, classBytes, null);
}

可以看到,CallSite的生成过程中,实际上时使用ASM框架动态构造一个匿名类,进行调用。

5. 总结

本文介绍了Java8中的Lambda表达式写法、函数接口以及Lambda的调用过程。Lambda与匿名内部类相似,但在调用过程中略有不同,匿名内部类在编译过程中会生成对于的匿名类文件,而Lambda表达式则是在运行时,通过特有的InvokeDynamic java虚拟机指令来通过包含asm动态生成类的动态调用点CallSite进行调用

附录:Class文件内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
Classfile /Users/***/out/production/classes/cn/cwiki/java8/TestLambda.class
Last modified 2019-6-2; size 3691 bytes
MD5 checksum 5a0ff75b4d8bc1fc577e30389b42d89c
Compiled from "TestLambda.java"
public class cn.cwiki.java8.TestLambda
minor version: 0
major version: 52
flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
#1 = Methodref #39.#83 // java/lang/Object."<init>":()V
#2 = InvokeDynamic #0:#89 // #0:test:()Lcn/cwiki/java8/TestLambda$TestFun;
#3 = Fieldref #90.#91 // java/lang/System.out:Ljava/io/PrintStream;
#4 = Methodref #92.#93 // java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
#5 = InterfaceMethodref #40.#94 // cn/cwiki/java8/TestLambda$TestFun.test:(Ljava/lang/Object;)Ljava/lang/Object;
#6 = Methodref #95.#96 // java/io/PrintStream.println:(Ljava/lang/Object;)V
#7 = Class #97 // cn/cwiki/java8/TestLambda
#8 = Methodref #7.#83 // cn/cwiki/java8/TestLambda."<init>":()V
#9 = Class #98 // cn/cwiki/java8/TestLambda$1
#10 = Methodref #9.#83 // cn/cwiki/java8/TestLambda$1."<init>":()V
#11 = Methodref #7.#99 // cn/cwiki/java8/TestLambda.test:(Lcn/cwiki/java8/TestLambda$OneArgument;)V
#12 = Methodref #39.#100 // java/lang/Object.getClass:()Ljava/lang/Class;
#13 = InvokeDynamic #1:#103 // #1:oneArg:(Ljava/io/PrintStream;)Lcn/cwiki/java8/TestLambda$OneArgument;
#14 = InvokeDynamic #2:#109 // #2:noArg:()Lcn/cwiki/java8/TestLambda$NoArgument;
#15 = Methodref #7.#110 // cn/cwiki/java8/TestLambda.testSeri:(Lcn/cwiki/java8/TestLambda$NoArgument;)V
#16 = InterfaceMethodref #45.#111 // cn/cwiki/java8/TestLambda$OneArgument.oneArg:(I)V
#17 = InterfaceMethodref #43.#112 // cn/cwiki/java8/TestLambda$NoArgument.noArg:()V
#18 = Methodref #113.#114 // java/lang/invoke/SerializedLambda.getImplMethodName:()Ljava/lang/String;
#19 = Methodref #115.#116 // java/lang/String.hashCode:()I
#20 = String #76 // lambda$main$22abaf9d$1
#21 = Methodref #115.#117 // java/lang/String.equals:(Ljava/lang/Object;)Z
#22 = Methodref #113.#118 // java/lang/invoke/SerializedLambda.getImplMethodKind:()I
#23 = Methodref #113.#119 // java/lang/invoke/SerializedLambda.getFunctionalInterfaceClass:()Ljava/lang/String;
#24 = String #120 // cn/cwiki/java8/TestLambda$NoArgument
#25 = Methodref #39.#117 // java/lang/Object.equals:(Ljava/lang/Object;)Z
#26 = Methodref #113.#121 // java/lang/invoke/SerializedLambda.getFunctionalInterfaceMethodName:()Ljava/lang/String;
#27 = String #122 // noArg
#28 = Methodref #113.#123 // java/lang/invoke/SerializedLambda.getFunctionalInterfaceMethodSignature:()Ljava/lang/String;
#29 = String #48 // ()V
#30 = Methodref #113.#124 // java/lang/invoke/SerializedLambda.getImplClass:()Ljava/lang/String;
#31 = String #97 // cn/cwiki/java8/TestLambda
#32 = Methodref #113.#125 // java/lang/invoke/SerializedLambda.getImplMethodSignature:()Ljava/lang/String;
#33 = Class #126 // java/lang/IllegalArgumentException
#34 = String #127 // Invalid lambda deserialization
#35 = Methodref #33.#128 // java/lang/IllegalArgumentException."<init>":(Ljava/lang/String;)V
#36 = String #129 // Serializable
#37 = Methodref #95.#130 // java/io/PrintStream.println:(Ljava/lang/String;)V
#38 = Methodref #92.#131 // java/lang/Integer.intValue:()I
#39 = Class #132 // java/lang/Object
#40 = Class #133 // cn/cwiki/java8/TestLambda$TestFun
#41 = Utf8 TestFun
#42 = Utf8 InnerClasses
#43 = Class #120 // cn/cwiki/java8/TestLambda$NoArgument
#44 = Utf8 NoArgument
#45 = Class #134 // cn/cwiki/java8/TestLambda$OneArgument
#46 = Utf8 OneArgument
#47 = Utf8 <init>
#48 = Utf8 ()V
#49 = Utf8 Code
#50 = Utf8 LineNumberTable
#51 = Utf8 LocalVariableTable
#52 = Utf8 this
#53 = Utf8 Lcn/cwiki/java8/TestLambda;
#54 = Utf8 main
#55 = Utf8 ([Ljava/lang/String;)V
#56 = Utf8 args
#57 = Utf8 [Ljava/lang/String;
#58 = Utf8 tf
#59 = Utf8 Lcn/cwiki/java8/TestLambda$TestFun;
#60 = Utf8 LocalVariableTypeTable
#61 = Utf8 Lcn/cwiki/java8/TestLambda$TestFun<Ljava/lang/Integer;Ljava/lang/Integer;>;
#62 = Utf8 test
#63 = Utf8 (Lcn/cwiki/java8/TestLambda$OneArgument;)V
#64 = Utf8 oneArgument
#65 = Utf8 Lcn/cwiki/java8/TestLambda$OneArgument;
#66 = Utf8 testSeri
#67 = Utf8 (Lcn/cwiki/java8/TestLambda$NoArgument;)V
#68 = Utf8 noArgument
#69 = Utf8 Lcn/cwiki/java8/TestLambda$NoArgument;
#70 = Utf8 $deserializeLambda$
#71 = Utf8 (Ljava/lang/invoke/SerializedLambda;)Ljava/lang/Object;
#72 = Utf8 lambda
#73 = Utf8 Ljava/lang/invoke/SerializedLambda;
#74 = Utf8 StackMapTable
#75 = Class #135 // java/lang/String
#76 = Utf8 lambda$main$22abaf9d$1
#77 = Utf8 lambda$main$0
#78 = Utf8 (Ljava/lang/Integer;)Ljava/lang/Integer;
#79 = Utf8 a
#80 = Utf8 Ljava/lang/Integer;
#81 = Utf8 SourceFile
#82 = Utf8 TestLambda.java
#83 = NameAndType #47:#48 // "<init>":()V
#84 = Utf8 BootstrapMethods
#85 = MethodHandle #6:#136 // invokestatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
#86 = MethodType #137 // (Ljava/lang/Object;)Ljava/lang/Object;
#87 = MethodHandle #6:#138 // invokestatic cn/cwiki/java8/TestLambda.lambda$main$0:(Ljava/lang/Integer;)Ljava/lang/Integer;
#88 = MethodType #78 // (Ljava/lang/Integer;)Ljava/lang/Integer;
#89 = NameAndType #62:#139 // test:()Lcn/cwiki/java8/TestLambda$TestFun;
#90 = Class #140 // java/lang/System
#91 = NameAndType #141:#142 // out:Ljava/io/PrintStream;
#92 = Class #143 // java/lang/Integer
#93 = NameAndType #144:#145 // valueOf:(I)Ljava/lang/Integer;
#94 = NameAndType #62:#137 // test:(Ljava/lang/Object;)Ljava/lang/Object;
#95 = Class #146 // java/io/PrintStream
#96 = NameAndType #147:#148 // println:(Ljava/lang/Object;)V
#97 = Utf8 cn/cwiki/java8/TestLambda
#98 = Utf8 cn/cwiki/java8/TestLambda$1
#99 = NameAndType #62:#63 // test:(Lcn/cwiki/java8/TestLambda$OneArgument;)V
#100 = NameAndType #149:#150 // getClass:()Ljava/lang/Class;
#101 = MethodType #151 // (I)V
#102 = MethodHandle #5:#152 // invokevirtual java/io/PrintStream.println:(I)V
#103 = NameAndType #153:#154 // oneArg:(Ljava/io/PrintStream;)Lcn/cwiki/java8/TestLambda$OneArgument;
#104 = MethodHandle #6:#155 // invokestatic java/lang/invoke/LambdaMetafactory.altMetafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;[Ljava/lang/Object;)Ljava/lang/invoke/CallSite;
#105 = MethodType #48 // ()V
#106 = MethodHandle #6:#156 // invokestatic cn/cwiki/java8/TestLambda.lambda$main$22abaf9d$1:()V
#107 = Integer 5
#108 = Integer 0
#109 = NameAndType #122:#157 // noArg:()Lcn/cwiki/java8/TestLambda$NoArgument;
#110 = NameAndType #66:#67 // testSeri:(Lcn/cwiki/java8/TestLambda$NoArgument;)V
#111 = NameAndType #153:#151 // oneArg:(I)V
#112 = NameAndType #122:#48 // noArg:()V
#113 = Class #158 // java/lang/invoke/SerializedLambda
#114 = NameAndType #159:#160 // getImplMethodName:()Ljava/lang/String;
#115 = Class #135 // java/lang/String
#116 = NameAndType #161:#162 // hashCode:()I
#117 = NameAndType #163:#164 // equals:(Ljava/lang/Object;)Z
#118 = NameAndType #165:#162 // getImplMethodKind:()I
#119 = NameAndType #166:#160 // getFunctionalInterfaceClass:()Ljava/lang/String;
#120 = Utf8 cn/cwiki/java8/TestLambda$NoArgument
#121 = NameAndType #167:#160 // getFunctionalInterfaceMethodName:()Ljava/lang/String;
#122 = Utf8 noArg
#123 = NameAndType #168:#160 // getFunctionalInterfaceMethodSignature:()Ljava/lang/String;
#124 = NameAndType #169:#160 // getImplClass:()Ljava/lang/String;
#125 = NameAndType #170:#160 // getImplMethodSignature:()Ljava/lang/String;
#126 = Utf8 java/lang/IllegalArgumentException
#127 = Utf8 Invalid lambda deserialization
#128 = NameAndType #47:#171 // "<init>":(Ljava/lang/String;)V
#129 = Utf8 Serializable
#130 = NameAndType #147:#171 // println:(Ljava/lang/String;)V
#131 = NameAndType #172:#162 // intValue:()I
#132 = Utf8 java/lang/Object
#133 = Utf8 cn/cwiki/java8/TestLambda$TestFun
#134 = Utf8 cn/cwiki/java8/TestLambda$OneArgument
#135 = Utf8 java/lang/String
#136 = Methodref #173.#174 // java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
#137 = Utf8 (Ljava/lang/Object;)Ljava/lang/Object;
#138 = Methodref #7.#175 // cn/cwiki/java8/TestLambda.lambda$main$0:(Ljava/lang/Integer;)Ljava/lang/Integer;
#139 = Utf8 ()Lcn/cwiki/java8/TestLambda$TestFun;
#140 = Utf8 java/lang/System
#141 = Utf8 out
#142 = Utf8 Ljava/io/PrintStream;
#143 = Utf8 java/lang/Integer
#144 = Utf8 valueOf
#145 = Utf8 (I)Ljava/lang/Integer;
#146 = Utf8 java/io/PrintStream
#147 = Utf8 println
#148 = Utf8 (Ljava/lang/Object;)V
#149 = Utf8 getClass
#150 = Utf8 ()Ljava/lang/Class;
#151 = Utf8 (I)V
#152 = Methodref #95.#176 // java/io/PrintStream.println:(I)V
#153 = Utf8 oneArg
#154 = Utf8 (Ljava/io/PrintStream;)Lcn/cwiki/java8/TestLambda$OneArgument;
#155 = Methodref #173.#177 // java/lang/invoke/LambdaMetafactory.altMetafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;[Ljava/lang/Object;)Ljava/lang/invoke/CallSite;
#156 = Methodref #7.#178 // cn/cwiki/java8/TestLambda.lambda$main$22abaf9d$1:()V
#157 = Utf8 ()Lcn/cwiki/java8/TestLambda$NoArgument;
#158 = Utf8 java/lang/invoke/SerializedLambda
#159 = Utf8 getImplMethodName
#160 = Utf8 ()Ljava/lang/String;
#161 = Utf8 hashCode
#162 = Utf8 ()I
#163 = Utf8 equals
#164 = Utf8 (Ljava/lang/Object;)Z
#165 = Utf8 getImplMethodKind
#166 = Utf8 getFunctionalInterfaceClass
#167 = Utf8 getFunctionalInterfaceMethodName
#168 = Utf8 getFunctionalInterfaceMethodSignature
#169 = Utf8 getImplClass
#170 = Utf8 getImplMethodSignature
#171 = Utf8 (Ljava/lang/String;)V
#172 = Utf8 intValue
#173 = Class #179 // java/lang/invoke/LambdaMetafactory
#174 = NameAndType #180:#183 // metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
#175 = NameAndType #77:#78 // lambda$main$0:(Ljava/lang/Integer;)Ljava/lang/Integer;
#176 = NameAndType #147:#151 // println:(I)V
#177 = NameAndType #184:#185 // altMetafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;[Ljava/lang/Object;)Ljava/lang/invoke/CallSite;
#178 = NameAndType #76:#48 // lambda$main$22abaf9d$1:()V
#179 = Utf8 java/lang/invoke/LambdaMetafactory
#180 = Utf8 metafactory
#181 = Class #187 // java/lang/invoke/MethodHandles$Lookup
#182 = Utf8 Lookup
#183 = Utf8 (Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
#184 = Utf8 altMetafactory
#185 = Utf8 (Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;[Ljava/lang/Object;)Ljava/lang/invoke/CallSite;
#186 = Class #188 // java/lang/invoke/MethodHandles
#187 = Utf8 java/lang/invoke/MethodHandles$Lookup
#188 = Utf8 java/lang/invoke/MethodHandles
{
public cn.cwiki.java8.TestLambda();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
LineNumberTable:
line 5: 0
LocalVariableTable:
Start Length Slot Name Signature
0 5 0 this Lcn/cwiki/java8/TestLambda;

public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=3, locals=2, args_size=1
0: invokedynamic #2, 0 // InvokeDynamic #0:test:()Lcn/cwiki/java8/TestLambda$TestFun;
5: astore_1
6: getstatic #3 // Field java/lang/System.out:Ljava/io/PrintStream;
9: aload_1
10: bipush 10
12: invokestatic #4 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
15: invokeinterface #5, 2 // InterfaceMethod cn/cwiki/java8/TestLambda$TestFun.test:(Ljava/lang/Object;)Ljava/lang/Object;
20: invokevirtual #6 // Method java/io/PrintStream.println:(Ljava/lang/Object;)V
23: new #7 // class cn/cwiki/java8/TestLambda
26: dup
27: invokespecial #8 // Method "<init>":()V
30: new #9 // class cn/cwiki/java8/TestLambda$1
33: dup
34: invokespecial #10 // Method cn/cwiki/java8/TestLambda$1."<init>":()V
37: invokevirtual #11 // Method test:(Lcn/cwiki/java8/TestLambda$OneArgument;)V
40: new #7 // class cn/cwiki/java8/TestLambda
43: dup
44: invokespecial #8 // Method "<init>":()V
47: getstatic #3 // Field java/lang/System.out:Ljava/io/PrintStream;
50: dup
51: invokevirtual #12 // Method java/lang/Object.getClass:()Ljava/lang/Class;
54: pop
55: invokedynamic #13, 0 // InvokeDynamic #1:oneArg:(Ljava/io/PrintStream;)Lcn/cwiki/java8/TestLambda$OneArgument;
60: invokevirtual #11 // Method test:(Lcn/cwiki/java8/TestLambda$OneArgument;)V
63: new #7 // class cn/cwiki/java8/TestLambda
66: dup
67: invokespecial #8 // Method "<init>":()V
70: invokedynamic #14, 0 // InvokeDynamic #2:noArg:()Lcn/cwiki/java8/TestLambda$NoArgument;
75: invokevirtual #15 // Method testSeri:(Lcn/cwiki/java8/TestLambda$NoArgument;)V
78: return
LineNumberTable:
line 7: 0
line 8: 6
line 9: 23
line 15: 40
line 16: 63
line 19: 78
LocalVariableTable:
Start Length Slot Name Signature
0 79 0 args [Ljava/lang/String;
6 73 1 tf Lcn/cwiki/java8/TestLambda$TestFun;
LocalVariableTypeTable:
Start Length Slot Name Signature
6 73 1 tf Lcn/cwiki/java8/TestLambda$TestFun<Ljava/lang/Integer;Ljava/lang/Integer;>;

void test(cn.cwiki.java8.TestLambda$OneArgument);
descriptor: (Lcn/cwiki/java8/TestLambda$OneArgument;)V
flags:
Code:
stack=2, locals=2, args_size=2
0: aload_1
1: bipush 100
3: invokeinterface #16, 2 // InterfaceMethod cn/cwiki/java8/TestLambda$OneArgument.oneArg:(I)V
8: return
LineNumberTable:
line 22: 0
line 23: 8
LocalVariableTable:
Start Length Slot Name Signature
0 9 0 this Lcn/cwiki/java8/TestLambda;
0 9 1 oneArgument Lcn/cwiki/java8/TestLambda$OneArgument;

void testSeri(cn.cwiki.java8.TestLambda$NoArgument);
descriptor: (Lcn/cwiki/java8/TestLambda$NoArgument;)V
flags:
Code:
stack=1, locals=2, args_size=2
0: aload_1
1: invokeinterface #17, 1 // InterfaceMethod cn/cwiki/java8/TestLambda$NoArgument.noArg:()V
6: return
LineNumberTable:
line 26: 0
line 27: 6
LocalVariableTable:
Start Length Slot Name Signature
0 7 0 this Lcn/cwiki/java8/TestLambda;
0 7 1 noArgument Lcn/cwiki/java8/TestLambda$NoArgument;
}
SourceFile: "TestLambda.java"
InnerClasses:
public static #41= #40 of #7; //TestFun=class cn/cwiki/java8/TestLambda$TestFun of class cn/cwiki/java8/TestLambda
public static #44= #43 of #7; //NoArgument=class cn/cwiki/java8/TestLambda$NoArgument of class cn/cwiki/java8/TestLambda
public static #46= #45 of #7; //OneArgument=class cn/cwiki/java8/TestLambda$OneArgument of class cn/cwiki/java8/TestLambda
static #9; //class cn/cwiki/java8/TestLambda$1
public static final #182= #181 of #186; //Lookup=class java/lang/invoke/MethodHandles$Lookup of class java/lang/invoke/MethodHandles
BootstrapMethods:
0: #85 invokestatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
Method arguments:
#86 (Ljava/lang/Object;)Ljava/lang/Object;
#87 invokestatic cn/cwiki/java8/TestLambda.lambda$main$0:(Ljava/lang/Integer;)Ljava/lang/Integer;
#88 (Ljava/lang/Integer;)Ljava/lang/Integer;
1: #85 invokestatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
Method arguments:
#101 (I)V
#102 invokevirtual java/io/PrintStream.println:(I)V
#101 (I)V
2: #104 invokestatic java/lang/invoke/LambdaMetafactory.altMetafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;[Ljava/lang/Object;)Ljava/lang/invoke/CallSite;
Method arguments:
#105 ()V
#106 invokestatic cn/cwiki/java8/TestLambda.lambda$main$22abaf9d$1:()V
#105 ()V
#107 5
#108 0

一些指令:

invokevirtual 指令用于调用对象的实例方法

invokespecial 调用一些需要特殊处理的实例方法,如初始化方法、父方法和私有方法

invokestatic 调用调用类方法

invokeinterface 调用接口方法

引用