前言
lambda表达式我们都用过,但是字节码层面Java是如何处理lambda这个语法糖的呢?Android上是否有对lambda做特殊处理?带着这俩问题,本文将以几段代码作为示例为你揭开lambda背后的黑科技。
invokedynamic on JVM
以这段代码为例:1
2
3
4
5
6
7
8
9
10class Test {
public static void main(String[] args) {
Consumer<String> consumer = s -> System.out.println(s);
consumer.accept("hello world");
}
interface Consumer<T> {
void accept(T t);
}
}
这是个最简单的带有一个参数的lambda,通过命令:1
2➜ javac Test.java
➜ javap -verbose Test
即可查看其字节码,可以看到main方法字节码如下:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=2, args_size=1
0: invokedynamic #2, 0 // InvokeDynamic #0:accept:()LTest$Consumer;
5: astore_1
6: aload_1
7: ldc #3 // String hello world
9: invokeinterface #4, 2 // InterfaceMethod Test$Consumer.accept:(Ljava/lang/Object;)V
14: return
LineNumberTable:
line 12: 0
line 13: 6
line 14: 14
不难看懂,首先通过invokedynamic返回了一个Consumer实例,然后将该实例从操作数栈存入局部变量表第2个变量,接着从局部变量表把该实例推到操作数栈,把常量”hello world”推到操作数栈,通过invokeinterface调用该实例的accept方法。
invokedynamic其实调的是BootstrapMethod
:1
2
3
4
5
6
7
8
9InnerClasses:
static #10= #9 of #7; //Consumer=class Test$Consumer of class Test
public static final #52= #51 of #54; //Lookup=class java/lang/invoke/MethodHandles$Lookup of class java/lang/invoke/MethodHandles
BootstrapMethods:
0: #24 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:
#25 (Ljava/lang/Object;)V
#26 invokestatic Test.lambda$main$0:(Ljava/lang/Object;)V
#25 (Ljava/lang/Object;)V
可见实际上invokedynamic调的是LambdaMetafactory.metafactory
方法。为了方便看代码,我直接用cfr decompile class还原desugaring后的Java代码: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➜ java -jar /Users/denglin03/Downloads/cfr-0.146.jar /Users/denglin03/Desktop/Test.class --decodelambdas false
/*
* Decompiled with CFR 0.146.
*/
import java.io.PrintStream;
import java.lang.invoke.LambdaMetafactory;
class Test {
Test() {
}
public static void main(String[] arrstring) {
Consumer<Object> consumer = (Consumer<Object>)LambdaMetafactory.metafactory(null, null, null, (Ljava/lang/Object;)V, lambda$main$0(java.lang.Object ), (Ljava/lang/Object;)V)();
consumer.accept("hello world");
}
private static /* synthetic */ void lambda$main$0(Object object) {
System.out.println(object);
}
static interface Consumer<T> {
public void accept(T var1);
}
}
确认调的是LambdaMetafactory.metafactory
无疑,jdk源码找到LambdaMetafactory
源码: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/**
* Facilitates the creation of simple "function objects" that implement one
* or more interfaces by delegation to a provided {@link MethodHandle},
* after appropriate type adaptation and partial evaluation of arguments.
* Typically used as a <em>bootstrap method</em> for {@code invokedynamic}
* call sites, to support the <em>lambda expression</em> and <em>method
* reference expression</em> features of the Java Programming Language.
*
* <p>This is the standard, streamlined metafactory; additional flexibility
* is provided by {@link #altMetafactory(MethodHandles.Lookup, String, MethodType, Object...)}.
* A general description of the behavior of this method is provided
* {@link LambdaMetafactory above}.
*
* <p>When the target of the {@code CallSite} returned from this method is
* invoked, the resulting function objects are instances of a class which
* implements the interface named by the return type of {@code invokedType},
* declares a method with the name given by {@code invokedName} and the
* signature given by {@code samMethodType}. It may also override additional
* methods from {@code Object}.
*
* @param caller Represents a lookup context with the accessibility
* privileges of the caller. When used with {@code invokedynamic},
* this is stacked automatically by the VM.
* @param invokedName The name of the method to implement. When used with
* {@code invokedynamic}, this is provided by the
* {@code NameAndType} of the {@code InvokeDynamic}
* structure and is stacked automatically by the VM.
* @param invokedType The expected signature of the {@code CallSite}. The
* parameter types represent the types of capture variables;
* the return type is the interface to implement. When
* used with {@code invokedynamic}, this is provided by
* the {@code NameAndType} of the {@code InvokeDynamic}
* structure and is stacked automatically by the VM.
* In the event that the implementation method is an
* instance method and this signature has any parameters,
* the first parameter in the invocation signature must
* correspond to the receiver.
* @param samMethodType Signature and return type of method to be implemented
* by the function object.
* @param implMethod A direct method handle describing the implementation
* method which should be called (with suitable adaptation
* of argument types, return types, and with captured
* arguments prepended to the invocation arguments) at
* invocation time.
* @param instantiatedMethodType The signature and return type that should
* be enforced dynamically at invocation time.
* This may be the same as {@code samMethodType},
* or may be a specialization of it.
* @return a CallSite whose target can be used to perform capture, generating
* instances of the interface named by {@code invokedType}
* @throws LambdaConversionException If any of the linkage invariants
* described {@link LambdaMetafactory above}
* are violated
*/
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();
}
主要是调了InnerClassLambdaMetafactory
的buildCallSite
方法返回类一个callsite: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/**
* Build the CallSite. Generate a class file which implements the functional
* interface, define the class, if there are no parameters create an instance
* of the class which the CallSite will return, otherwise, generate handles
* which will call the class' constructor.
*
* @return a CallSite, which, when invoked, will return an instance of the
* functional interface
* @throws ReflectiveOperationException
* @throws LambdaConversionException If properly formed functional interface
* is not found
*/
CallSite buildCallSite() throws LambdaConversionException {
final Class<?> innerClass = spinInnerClass();
if (invokedType.parameterCount() == 0) {
final Constructor<?>[] ctrs = AccessController.doPrivileged(
new PrivilegedAction<Constructor<?>[]>() {
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);
}
}
}
callsite是methodHandle的holder,用于将invokedynamic指令委托给methodHandle执行,这个methodhandle指向的就是spinInnerClass()
生成的实现了我们lambda对应的functional interface的实例的某个方法,这个方法参数类型转换后直接调用demo中的lambda$main$0
方法1
2
3private static /* synthetic */ void lambda$main$0(Object object) {
System.out.println(object);
}
可以从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
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96/**
* Generate a class file which implements the functional
* interface, define and return the class.
*
* @implNote The class that is generated does not include signature
* information for exceptions that may be present on the SAM method.
* This is to reduce classfile size, and is harmless as checked exceptions
* are erased anyway, no one will ever compile against this classfile,
* and we make no guarantees about the reflective properties of lambda
* objects.
*
* @return a Class which implements the functional interface
* @throws LambdaConversionException If properly formed functional interface
* is not found
*/
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()]);
}
// 使用ASM生成lambdaClassName这个类
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);
// 生成调用implMethod方法的方法
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>() {
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);
}
其中lambdaClassName和implMethod取值如下:1
2lambdaClassName = targetClass.getName().replace('.', '/') + "$$Lambda$" + counter.incrementAndGet();
// implMethod就是metafactory的第五个参数:lambda$main$0(java.lang.Object )
到这里我们已经大致知道lambda在JVM背后的黑科技了,通过invokedynamic导向jdk中的实现callsite,jdk中通过ASM生成对应的lambda类,原caller类中插入新的static method,负责lambda具体逻辑。callsite调用指向methodhandle,进而调到lambda类中的方法,然后调到lambda具体逻辑。这样做的好处其实主要还是灵活,一方面减少字节码大小,另一方面后续变更实现的话不必增加新的opcode,在jdk中更新即可。可以看到由于本例中lambda只有一个参数,所以生成的static method也只有一个参数:1
2
3private static /* synthetic */ void lambda$main$0(Object object) {
System.out.println(object);
}
但是如果lambda中使用类外部变量的话,即使lambda只有一个参数,lambda中每一个用到的外部变量也会作为一个独立的参数加到static method参数中。
D8 on Android
前面我们分析了JVM上lambda是通过invokedynamic指令调用java.lang.invoke包下LambdaMetafactory的metafactory方法来实现的。但是Android Runtime并没有LambdaMetafactory这个类,相当于JVM那套在Android上行不通。详见官方文档和AOSP源码。那Android上如何处理lambda?
我们试试用dx把class转dex分析:1
2
3
4
5
6➜ dx --dex --output . *.class
Uncaught translation error: com.android.dx.cf.code.SimException:
ERROR in Test.main:([Ljava/lang/String;)V:
invalid opcode ba - invokedynamic requires --min-sdk-version >= 26
(currently 13)
1 error; aborting
可见android在API26后才支持invokedynamic指令。那么在低版本的Android上如何保证兼容性呢?
desugaring
在dx时代,desugaring和proguard一样,属于独立的transformation,在dx将class转dex之前已经执行完了desugaring。而d8 dexer则包括了desugaring功能,我们同样使用d8来把class转dex看看。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➜ d8 --output . Test.class Test\$Consumer.class
➜ dexdump -d classes.dex
Processing 'classes.dex'...
Opened 'classes.dex', DEX version '035'
Class #0 -
Class descriptor : 'LTest$Consumer;'
Access flags : 0x0600 (INTERFACE ABSTRACT)
Superclass : 'Ljava/lang/Object;'
Interfaces -
Static fields -
Instance fields -
Direct methods -
Virtual methods -
#0 : (in LTest$Consumer;)
name : 'accept'
type : '(Ljava/lang/Object;)V'
access : 0x0401 (PUBLIC ABSTRACT)
code : (none)
source_file_idx : 17 (Test.java)
Class #1 -
Class descriptor : 'LTest;'
Access flags : 0x0000 ()
Superclass : 'Ljava/lang/Object;'
Interfaces -
Static fields -
Instance fields -
Direct methods -
#0 : (in LTest;)
name : '<init>'
type : '()V'
access : 0x10000 (CONSTRUCTOR)
code -
registers : 1
ins : 1
outs : 1
insns size : 4 16-bit code units
00024c: |[00024c] Test.<init>:()V
00025c: 7010 0800 0000 |0000: invoke-direct {v0}, Ljava/lang/Object;.<init>:()V // method@0008
000262: 0e00 |0003: return-void
catches : (none)
positions :
0x0000 line=3
locals :
0x0000 - 0x0004 reg=0 this LTest;
#1 : (in LTest;)
name : 'lambda$main$0'
type : '(Ljava/lang/Object;)V'
access : 0x1008 (STATIC SYNTHETIC)
code -
registers : 2
ins : 1
outs : 2
insns size : 6 16-bit code units
000264: |[000264] Test.lambda$main$0:(Ljava/lang/Object;)V
000274: 6200 0100 |0000: sget-object v0, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0001
000278: 6e20 0700 1000 |0002: invoke-virtual {v0, v1}, Ljava/io/PrintStream;.println:(Ljava/lang/Object;)V // method@0007
00027e: 0e00 |0005: return-void
catches : (none)
positions :
0x0000 line=12
locals :
#2 : (in LTest;)
name : 'main'
type : '([Ljava/lang/String;)V'
access : 0x0009 (PUBLIC STATIC)
code -
registers : 2
ins : 1
outs : 2
insns size : 8 16-bit code units
000280: |[000280] Test.main:([Ljava/lang/String;)V
000290: 6201 0000 |0000: sget-object v1, L-$$Lambda$Test$VtEj3BunDEASiYmhFnrh0idYOr0;.INSTANCE:L-$$Lambda$Test$VtEj3BunDEASiYmhFnrh0idYOr0; // field@0000
000294: 1a00 1700 |0002: const-string v0, "hello world" // string@0017
000298: 7220 0300 0100 |0004: invoke-interface {v1, v0}, LTest$Consumer;.accept:(Ljava/lang/Object;)V // method@0003
00029e: 0e00 |0007: return-void
catches : (none)
positions :
0x0000 line=12
0x0002 line=13
0x0007 line=14
locals :
Virtual methods -
source_file_idx : 17 (Test.java)
Class #2 -
Class descriptor : 'L-$$Lambda$Test$VtEj3BunDEASiYmhFnrh0idYOr0;'
Access flags : 0x1011 (PUBLIC FINAL SYNTHETIC)
Superclass : 'Ljava/lang/Object;'
Interfaces -
#0 : 'LTest$Consumer;'
Static fields -
#0 : (in L-$$Lambda$Test$VtEj3BunDEASiYmhFnrh0idYOr0;)
name : 'INSTANCE'
type : 'L-$$Lambda$Test$VtEj3BunDEASiYmhFnrh0idYOr0;'
access : 0x1019 (PUBLIC STATIC FINAL SYNTHETIC)
Instance fields -
Direct methods -
#0 : (in L-$$Lambda$Test$VtEj3BunDEASiYmhFnrh0idYOr0;)
name : '<clinit>'
type : '()V'
access : 0x11008 (STATIC SYNTHETIC CONSTRUCTOR)
code -
registers : 1
ins : 0
outs : 1
insns size : 8 16-bit code units
0001fc: |[0001fc] -..Lambda.Test.VtEj3BunDEASiYmhFnrh0idYOr0.<clinit>:()V
00020c: 2200 0000 |0000: new-instance v0, L-$$Lambda$Test$VtEj3BunDEASiYmhFnrh0idYOr0; // type@0000
000210: 7010 0100 0000 |0002: invoke-direct {v0}, L-$$Lambda$Test$VtEj3BunDEASiYmhFnrh0idYOr0;.<init>:()V // method@0001
000216: 6900 0000 |0005: sput-object v0, L-$$Lambda$Test$VtEj3BunDEASiYmhFnrh0idYOr0;.INSTANCE:L-$$Lambda$Test$VtEj3BunDEASiYmhFnrh0idYOr0; // field@0000
00021a: 0e00 |0007: return-void
catches : (none)
positions :
locals :
#1 : (in L-$$Lambda$Test$VtEj3BunDEASiYmhFnrh0idYOr0;)
name : '<init>'
type : '()V'
access : 0x11002 (PRIVATE SYNTHETIC CONSTRUCTOR)
code -
registers : 1
ins : 1
outs : 1
insns size : 4 16-bit code units
00021c: |[00021c] -..Lambda.Test.VtEj3BunDEASiYmhFnrh0idYOr0.<init>:()V
00022c: 7010 0800 0000 |0000: invoke-direct {v0}, Ljava/lang/Object;.<init>:()V // method@0008
000232: 0e00 |0003: return-void
catches : (none)
positions :
locals :
Virtual methods -
#0 : (in L-$$Lambda$Test$VtEj3BunDEASiYmhFnrh0idYOr0;)
name : 'accept'
type : '(Ljava/lang/Object;)V'
access : 0x0011 (PUBLIC FINAL)
code -
registers : 2
ins : 2
outs : 1
insns size : 4 16-bit code units
000234: |[000234] -..Lambda.Test.VtEj3BunDEASiYmhFnrh0idYOr0.accept:(Ljava/lang/Object;)V
000244: 7110 0500 0100 |0000: invoke-static {v1}, LTest;.lambda$main$0:(Ljava/lang/Object;)V // method@0005
00024a: 0e00 |0003: return-void
catches : (none)
positions :
locals :
source_file_idx : 24 (lambda)
可以看到desugaring之后仍然是lambda$main$0
方法负责具体逻辑,同时生成了一个-$$Lambda$Test$VtEj3BunDEASiYmhFnrh0idYOr0
类,其实现了Consumer接口,accept方法间接调用了Test类的lambda$main$0
方法,同时存在静态单例对象INSTANCE
。原Test类的main
方法调用-$$Lambda$Test$VtEj3BunDEASiYmhFnrh0idYOr0
类的INSTANCE
对象的accept方法传入”hello world”。
如果lambda中用到了外部变量呢?我们改下demo:1
2
3
4
5
6
7
8
9
10
11class Test {
public static void main(String[] args) {
String from = "lin";
Consumer consumer = s -> System.out.println(from + s);
consumer.accept("hello world");
}
interface Consumer<T> {
void accept(T t);
}
}
再次利用d8查看dex字节码可知: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// lambda$main$0方法新增了一个参数,继续负责lambda具体逻辑
0002c4: |[0002c4] Test.lambda$main$0:(Ljava/lang/String;Ljava/lang/Object;)V
0002d4: 6200 0100 |0000: sget-object v0, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0001
0002d8: 2201 0a00 |0002: new-instance v1, Ljava/lang/StringBuilder; // type@000a
0002dc: 7010 0800 0100 |0004: invoke-direct {v1}, Ljava/lang/StringBuilder;.<init>:()V // method@0008
0002e2: 6e20 0a00 2100 |0007: invoke-virtual {v1, v2}, Ljava/lang/StringBuilder;.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; // method@000a
0002e8: 6e20 0900 3100 |000a: invoke-virtual {v1, v3}, Ljava/lang/StringBuilder;.append:(Ljava/lang/Object;)Ljava/lang/StringBuilder; // method@0009
0002ee: 6e10 0b00 0100 |000d: invoke-virtual {v1}, Ljava/lang/StringBuilder;.toString:()Ljava/lang/String; // method@000b
0002f4: 0c02 |0010: move-result-object v2
0002f6: 6e20 0600 2000 |0011: invoke-virtual {v0, v2}, Ljava/io/PrintStream;.println:(Ljava/lang/String;)V // method@0006
0002fc: 0e00 |0014: return-void
catches : (none)
positions :
0x0000 line=13
locals :
#2 : (in LTest;)
name : 'main'
type : '([Ljava/lang/String;)V'
access : 0x0009 (PUBLIC STATIC)
code -
registers : 2
ins : 1
outs : 2
insns size : 13 16-bit code units
// main方法new -$$Lambda$Test$i6YS_4McUAo2DfQmb_xpDZoX2iw类实例的时候传入外部变量"lin" ,接着调实例的accept方法传入"hello world"
// 可以看到用了外部变量的情况下没有使用INSTANCE去调accept,而是new了一个实例,因为构造函数需要传入外部变量。
000300: |[000300] Test.main:([Ljava/lang/String;)V
000310: 1a01 1f00 |0000: const-string v1, "lin: " // string@001f
000314: 2200 0000 |0002: new-instance v0, L-$$Lambda$Test$i6YS_4McUAo2DfQmb_xpDZoX2iw; // type@0000
000318: 7020 0000 1000 |0004: invoke-direct {v0, v1}, L-$$Lambda$Test$i6YS_4McUAo2DfQmb_xpDZoX2iw;.<init>:(Ljava/lang/String;)V // method@0000
00031e: 1a01 1c00 |0007: const-string v1, "hello world" // string@001c
000322: 7220 0200 1000 |0009: invoke-interface {v0, v1}, LTest$Consumer;.accept:(Ljava/lang/Object;)V // method@0002
000328: 0e00 |000c: return-void
catches : (none)
positions :
0x0000 line=12
0x0002 line=13
0x0007 line=14
0x000c line=15
locals :
Virtual methods -
source_file_idx : 19 (Test.java)
Class #2 -
Class descriptor : 'L-$$Lambda$Test$i6YS_4McUAo2DfQmb_xpDZoX2iw;'
Access flags : 0x1011 (PUBLIC FINAL SYNTHETIC)
Superclass : 'Ljava/lang/Object;'
Interfaces -
#0 : 'LTest$Consumer;'
Static fields -
Instance fields -
#0 : (in L-$$Lambda$Test$i6YS_4McUAo2DfQmb_xpDZoX2iw;)
name : 'f$0'
type : 'Ljava/lang/String;'
access : 0x1012 (PRIVATE FINAL SYNTHETIC)
Direct methods -
#0 : (in L-$$Lambda$Test$i6YS_4McUAo2DfQmb_xpDZoX2iw;)
name : '<init>'
type : '(Ljava/lang/String;)V'
access : 0x11001 (PUBLIC SYNTHETIC CONSTRUCTOR)
code -
registers : 2
ins : 2
outs : 1
insns size : 6 16-bit code units
// 构造函数将传入的"lin"赋值给内部变量f$0
000274: |[000274] -..Lambda.Test.i6YS_4McUAo2DfQmb_xpDZoX2iw.<init>:(Ljava/lang/String;)V
000284: 7010 0700 0000 |0000: invoke-direct {v0}, Ljava/lang/Object;.<init>:()V // method@0007
00028a: 5b01 0000 |0003: iput-object v1, v0, L-$$Lambda$Test$i6YS_4McUAo2DfQmb_xpDZoX2iw;.f$0:Ljava/lang/String; // field@0000
00028e: 0e00 |0005: return-void
catches : (none)
positions :
locals :
Virtual methods -
#0 : (in L-$$Lambda$Test$i6YS_4McUAo2DfQmb_xpDZoX2iw;)
name : 'accept'
type : '(Ljava/lang/Object;)V'
access : 0x0011 (PUBLIC FINAL)
code -
registers : 3
ins : 2
outs : 2
insns size : 6 16-bit code units
// accept调用lambda$main$0方法的时候传入f$0和入参
000290: |[000290] -..Lambda.Test.i6YS_4McUAo2DfQmb_xpDZoX2iw.accept:(Ljava/lang/Object;)V
0002a0: 5410 0000 |0000: iget-object v0, v1, L-$$Lambda$Test$i6YS_4McUAo2DfQmb_xpDZoX2iw;.f$0:Ljava/lang/String; // field@0000
0002a4: 7120 0400 2000 |0002: invoke-static {v0, v2}, LTest;.lambda$main$0:(Ljava/lang/String;Ljava/lang/Object;)V // method@0004
0002aa: 0e00 |0005: return-void
catches : (none)
positions :
locals :
source_file_idx : 29 (lambda)
总体来看其实lambda在Android上的desugaring方式和匿名内部类很像,生成一个新类(以-$$Lambda$Test$i6YS_4McUAo2DfQmb_xpDZoX2iw为例),实现functional interface(Consumer)。然后生成一个static的lambda$$main$0方法,负责lambda具体逻辑。生成的新类重写接口的方法(accept),里面直接调lambda$$main$0方法。lambda的赋值和调用实际是对新类-$$Lambda$Test$i6YS_4McUAo2DfQmb_xpDZoX2iw的实例完成。
至此desugaring大致原理就清楚了。
参考: