Androi Studio 3.0及更高版本支持所有Java 7语言功能,以及部分Java 8语言功能(具体因平台版本而异)。
Java 8语言功能通过默认工具链中的Desugar支持,但Java 8语言API需要跟随Android SDK的版本,Android 7.0 (Sdk 24) 开始提供相关API支持。
Desugar目前暂不支持MethodHandle.invoke和MethodHandle.invokeExact,如果需要使用这两个方法,则需要指定miniSdkVersion 26或更高。
Retrolambda
Retrolambda通过JVM的一些机制(premain Agent ASM)在编译期把lambda表达式转换为内部类实现,但会生成类导致方法数增加。
Jack编译器
Google在Android SDK 21 (Android N 7.0)发布了新的编译器Jack/Jill来构建Android程序,但是他的实现机制是直接将Java源码转为dalvik字节码,而不是Java字节码。
随后Google发现这种实现方式带来的成本太大了,在Jack编译器之前Android程序的编译链是:
Java_Source-.Javac.->Java_ByteCode
Java_ByteCode-.dx.->dex
Jack后:
Java_Source-.Jack_Compiler.-dex
使用Jack直接将Java源码编译成了dex文件,Jack的推出是为了替代Javac的,Jill的作用是将依赖库.aar或者.jar转换成dex。
使用Jack,Google可以针对Android做更多的优化,而且还可以避免和Oracal的版权纠纷。Jack是Android 6.0-8.1的默认工具链,但google在Android 8.1之后废弃了Jack,原因原文
Over time, we realized the cost of switching to Jack was too high for our community when we considered the annotation processors, bytecode analyzers and rewriters impacted.
具体的原因无非是构建Java语言生态特性花费了大量的时间,比如注解处理器、字节码分析、重写和插桩工具,而且社区中已存在的基于这些基础设施的第三方库也无法平滑的切换到Jack。
题外话: APT注解处理器工具
在Android-Gradle-Tools 2.2版本前 Android没有提供默认的注解处理器,大部分工程都使用第三方开源的android-apt,在2.2版本之后,Google开始支持注解处理器annotationProcessor。
D8编译器
Jack编译器弃用滞后,Google在Android 3.1引入了d8编译器
graph LR
Java_Source-.Javac.->Java_ByteCode
Java_ByteCode-.D8.->dex
Lambda表达式
public class Java8 {
public static void main(String[] args) {
new Thread(()->System.out.println("test")).start();
}
}
javac java8.java //编译字节码
dx --dex --output=./Java8.dex Java8.class //转换成dex文件
Uncaught translation error: com.android.dx.cf.code.SimException: ERROR in Java8.main:([Ljava/lang/String;)V: invalid opcode ba - invokedynamic requires --min-sdk-version >= 26 (currently 13)
Lambda表达式在Java字节码层面使用了invokedynamic指令,而Android对指令invokedynamic在sdk版本大于26才支持,但Android并未直接使用invokedynamic指令,而是在Android 8.0 (Sdk 26) 升级了Dex格式,通过两个新的Dalvik指令invoke-polymorphic和invoke-custom来提供动态调用的支持。
可在Dalvik字节码查看指令更多细节
下面比较一下生成的字节码文件
字节码文件
javap -verbose Java8 //查看字节码
Classfile /Users/gentrio/Desktop/Java8.class
Last modified 2020-7-24; size 1043 bytes
MD5 checksum 6ddd92fd44495106d002b3595320fbd5
Compiled from "java8.java"
public class Java8
minor version: 0
major version: 52
flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
#1 = Methodref #10.#20 // java/lang/Object."<init>":()V
#2 = Class #21 // java/lang/Thread
#3 = InvokeDynamic #0:#26 // #0:run:()Ljava/lang/Runnable;
#4 = Methodref #2.#27 // java/lang/Thread."<init>":(Ljava/lang/Runnable;)V
#5 = Methodref #2.#28 // java/lang/Thread.start:()V
#6 = Fieldref #29.#30 // java/lang/System.out:Ljava/io/PrintStream;
#7 = String #31 // test
#8 = Methodref #32.#33 // java/io/PrintStream.println:(Ljava/lang/String;)V
#9 = Class #34 // Java8
#10 = Class #35 // java/lang/Object
#11 = Utf8 <init>
#12 = Utf8 ()V
#13 = Utf8 Code
#14 = Utf8 LineNumberTable
#15 = Utf8 main
#16 = Utf8 ([Ljava/lang/String;)V
#17 = Utf8 lambda$main$0
#18 = Utf8 SourceFile
#19 = Utf8 java8.java
#20 = NameAndType #11:#12 // "<init>":()V
#21 = Utf8 java/lang/Thread
#22 = Utf8 BootstrapMethods
#23 = MethodHandle #6:#36 // 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;
#24 = MethodType #12 // ()V
#25 = MethodHandle #6:#37 // invokestatic Java8.lambda$main$0:()V
#26 = NameAndType #38:#39 // run:()Ljava/lang/Runnable;
#27 = NameAndType #11:#40 // "<init>":(Ljava/lang/Runnable;)V
#28 = NameAndType #41:#12 // start:()V
#29 = Class #42 // java/lang/System
#30 = NameAndType #43:#44 // out:Ljava/io/PrintStream;
#31 = Utf8 test
#32 = Class #45 // java/io/PrintStream
#33 = NameAndType #46:#47 // println:(Ljava/lang/String;)V
#34 = Utf8 Java8
#35 = Utf8 java/lang/Object
#36 = Methodref #48.#49 // 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;
#37 = Methodref #9.#50 // Java8.lambda$main$0:()V
#38 = Utf8 run
#39 = Utf8 ()Ljava/lang/Runnable;
#40 = Utf8 (Ljava/lang/Runnable;)V
#41 = Utf8 start
#42 = Utf8 java/lang/System
#43 = Utf8 out
#44 = Utf8 Ljava/io/PrintStream;
#45 = Utf8 java/io/PrintStream
#46 = Utf8 println
#47 = Utf8 (Ljava/lang/String;)V
#48 = Class #51 // java/lang/invoke/LambdaMetafactory
#49 = NameAndType #52:#56 // 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;
#50 = NameAndType #17:#12 // lambda$main$0:()V
#51 = Utf8 java/lang/invoke/LambdaMetafactory
#52 = Utf8 metafactory
#53 = Class #58 // java/lang/invoke/MethodHandles$Lookup
#54 = Utf8 Lookup
#55 = Utf8 InnerClasses
#56 = 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;
#57 = Class #59 // java/lang/invoke/MethodHandles
#58 = Utf8 java/lang/invoke/MethodHandles$Lookup
#59 = Utf8 java/lang/invoke/MethodHandles
{
public Java8();
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 1: 0
public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=3, locals=1, args_size=1
0: new #2 // class java/lang/Thread
3: dup
4: invokedynamic #3, 0 // InvokeDynamic #0:run:()Ljava/lang/Runnable;
9: invokespecial #4 // Method java/lang/Thread."<init>":(Ljava/lang/Runnable;)V
12: invokevirtual #5 // Method java/lang/Thread.start:()V
15: return
LineNumberTable:
line 4: 0
line 5: 15
}
SourceFile: "java8.java"
InnerClasses:
public static final #54= #53 of #57; //Lookup=class java/lang/invoke/MethodHandles$Lookup of class java/lang/invoke/MethodHandles
BootstrapMethods:
0: #23 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:
#24 ()V
#25 invokestatic Java8.lambda$main$0:()V
#24 ()V
通过d8编译出来的dex文件
Processing 'classes.dex'...
Opened 'classes.dex', DEX version '035'
Class #0 -
Class descriptor : 'L-?Lambda$Java8$90vfg5nki1Mk2vmViVKTcPvg1UM;'
Access flags : 0x1011 (PUBLIC FINAL SYNTHETIC)
Superclass : 'Ljava/lang/Object;'
Interfaces -
#0 : 'Ljava/lang/Runnable;'
Static fields -
#0 : (in L-?Lambda$Java8$90vfg5nki1Mk2vmViVKTcPvg1UM;)
name : 'INSTANCE'
type : 'L-?Lambda$Java8$90vfg5nki1Mk2vmViVKTcPvg1UM;'
access : 0x1019 (PUBLIC STATIC FINAL SYNTHETIC)
Instance fields -
Direct methods -
#0 : (in L-?Lambda$Java8$90vfg5nki1Mk2vmViVKTcPvg1UM;)
name : '<clinit>'
type : '()V'
access : 0x11008 (STATIC SYNTHETIC CONSTRUCTOR)
code -
registers : 1
ins : 0
outs : 1
insns size : 8 16-bit code units
catches : (none)
positions :
locals :
#1 : (in L-?Lambda$Java8$90vfg5nki1Mk2vmViVKTcPvg1UM;)
name : '<init>'
type : '()V'
access : 0x11002 (PRIVATE SYNTHETIC CONSTRUCTOR)
code -
registers : 1
ins : 1
outs : 1
insns size : 4 16-bit code units
catches : (none)
positions :
locals :
Virtual methods -
#0 : (in L-?Lambda$Java8$90vfg5nki1Mk2vmViVKTcPvg1UM;)
name : 'run'
type : '()V'
access : 0x0011 (PUBLIC FINAL)
code -
registers : 1
ins : 1
outs : 0
insns size : 4 16-bit code units
catches : (none)
positions :
locals :
source_file_idx : 15 (lambda)
Class #1 -
Class descriptor : 'LJava8;'
Access flags : 0x0001 (PUBLIC)
Superclass : 'Ljava/lang/Object;'
Interfaces -
Static fields -
Instance fields -
Direct methods -
#0 : (in LJava8;)
name : '<init>'
type : '()V'
access : 0x10001 (PUBLIC CONSTRUCTOR)
code -
registers : 1
ins : 1
outs : 1
insns size : 4 16-bit code units
catches : (none)
positions :
0x0000 line=1
locals :
0x0000 - 0x0004 reg=0 this LJava8;
#1 : (in LJava8;)
name : 'lambda$main$0'
type : '()V'
access : 0x1008 (STATIC SYNTHETIC)
code -
registers : 2
ins : 0
outs : 2
insns size : 8 16-bit code units
catches : (none)
positions :
0x0000 line=4
locals :
#2 : (in LJava8;)
name : 'main'
type : '([Ljava/lang/String;)V'
access : 0x0009 (PUBLIC STATIC)
code -
registers : 2
ins : 1
outs : 2
insns size : 11 16-bit code units
catches : (none)
positions :
0x0000 line=4
0x000a line=5
locals :
Virtual methods -
source_file_idx : 14 (java8.java)
通过dx指定min-sdk-version=26编译出的dex文件
dexdump Java8-dx.dex
Processing 'Java8.dex'...
Opened 'Java8.dex', DEX version '038'
Class #0 -
Class descriptor : 'LJava8;'
Access flags : 0x0001 (PUBLIC)
Superclass : 'Ljava/lang/Object;'
Interfaces -
Static fields -
Instance fields -
Direct methods -
#0 : (in LJava8;)
name : '<init>'
type : '()V'
access : 0x10001 (PUBLIC CONSTRUCTOR)
code -
registers : 1
ins : 1
outs : 1
insns size : 4 16-bit code units
catches : (none)
positions :
0x0000 line=1
locals :
0x0000 - 0x0004 reg=0 this LJava8;
#1 : (in LJava8;)
name : 'lambda$main$0'
type : '()V'
access : 0x100a (PRIVATE STATIC SYNTHETIC)
code -
registers : 2
ins : 0
outs : 2
insns size : 8 16-bit code units
catches : (none)
positions :
0x0000 line=4
locals :
#2 : (in LJava8;)
name : 'main'
type : '([Ljava/lang/String;)V'
access : 0x0009 (PUBLIC STATIC)
code -
registers : 3
ins : 1
outs : 2
insns size : 13 16-bit code units
catches : (none)
positions :
0x0000 line=4
0x000c line=5
locals :
Virtual methods -
source_file_idx : 18 (java8.java)
Method Handle #0:
type : invoke-static
target : LJava8; lambda$main$0
target_type : ()V
Method Handle #1:
type : invoke-static
target : Ljava/lang/invoke/LambdaMetafactory; metafactory
target_type : (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;
Call Site #0 // offset 1059
link_argument[0] : 1 (MethodHandle)
link_argument[1] : run (String)
link_argument[2] : ()Ljava/lang/Runnable; (MethodType)
link_argument[3] : ()V (MethodType)
link_argument[4] : 0 (MethodHandle)
link_argument[5] : ()V (MethodType)
目前Android通过脱糖(Desugaring)的方式来实现所有版本设备的Lambda函数的支持。
脱糖 即在编译阶段将语法层面一些底层字节码不支持的特性转换成基础的字节码结构,(比如List上的泛型脱糖后在字节码层面实际为Object;
通过Java字节码可以发现Lambda在Jvm上是通过invokedynamic指令来实现的,但在dex字节码文件中却没有发现invokedynamic指令,下面分析一下jvm和dalvik上的区别:
invokedynamic指令
invokedynamic指令是Java 7新增的字节码调用指令,作为Java支持动态类型语言的改进之一,跟invokevirtual、invokestatic、invokeinterface、invokespecial指令构成了虚拟机层面的Java方法分配调用指令集。
- 后四种指令,在编译期间生成的class文件中,通过常量池(Constant Pool)的MethodRef常量已经固定了目标方法的符号信息,虚拟机使用符号信息能直接解释出具体的方法,直接调用。
- 而invokedynamic指令在编译期间生成的class文件中,对应常量池(Constant Pool)的invokedynamic_Info常量存储的符号信息中并没有方法所述者以及其类型,替代的是BoostrapMethod信息。在运行时,通过引导方法BootstrapMethod机制动态确定方法的所述者和类型。
Constant pool:
#1 = Methodref #10.#20 // java/lang/Object."<init>":()V
#2 = Class #21 // java/lang/Thread
#3 = InvokeDynamic #0:#26 // #0:run:()Ljava/lang/Runnable;
#4 = Methodref #2.#27 // java/lang/Thread."<init>":(Ljava/lang/Runnable;)V
#5 = Methodref #2.#28 // java/lang/Thread.start:()V
#6 = Fieldref #29.#30 // java/lang/System.out:Ljava/io/PrintStream;
#7 = String #31 // test
#8 = Methodref #32.#33 // java/io/PrintStream.println:(Ljava/lang/String;)V
#9 = Class #34 // Java8
#10 = Class #35 // java/lang/Object
#11 = Utf8 <init>
#12 = Utf8 ()V
#13 = Utf8 Code
#14 = Utf8 LineNumberTable
#15 = Utf8 main
#16 = Utf8 ([Ljava/lang/String;)V
#17 = Utf8 lambda$main$0
#18 = Utf8 SourceFile
#19 = Utf8 java8.java
#20 = NameAndType #11:#12 // "<init>":()V
#21 = Utf8 java/lang/Thread
#22 = Utf8 BootstrapMethods
#23 = MethodHandle #6:#36 // invokestatic
………………………………………………………………………………………………
BootstrapMethods:
0: #23 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:
#24 ()V
#25 invokestatic Java8.lambda$main$0:()V
#24 ()V
分析字节码可知,invokedynamic的调用步骤,编译时生成私有静态方法 -> 通过invokedynamic指令调用LambdaMetafactory.metafactory -> LambdaMetafactory.metafactory在内存中动态生成一个实现Lambda表达式对应函数式接口的实例并在实现中调用生成的私有静态方法
重点就在于LambdametaFactory.metafactory,通过查看Android SDK发现虽然在Android 7.0 SDK 24以后,Android运行时SDK中存在LambdametaFactory但却是空实现
package java.lang.invoke;
public class LambdaMetafactory {
public static final int FLAG_SERIALIZABLE = 1 << 0;
public static final int FLAG_MARKERS = 1 << 1;
public static final int FLAG_BRIDGES = 1 << 2;
public static CallSite metafactory(MethodHandles.Lookup caller,
String invokedName,
MethodType invokedType,
MethodType samMethodType,
MethodHandle implMethod,
MethodType instantiatedMethodType)
throws LambdaConversionException { return null; }
public static CallSite altMetafactory(MethodHandles.Lookup caller,
String invokedName,
MethodType invokedType,
Object... args)
throws LambdaConversionException { return null; }
}
为了验证LambdaMetafactory在运行时有没有具体实现,尝试在Android 10 SDK 29的机器上运行通过指定min-sdk-version的dx打出的dex包。
adb push Java8.dex /sdcard/
adb shell dalvikvm -cp /sdcard/Java8.dex Java8
Exception in thread "main" java.lang.BootstrapMethodError: Exception from call site #0 bootstrap method
at Java8.main(java8.java:4)
Caused by: java.lang.ClassCastException: Bootstrap method returned null
... 1 more
因此其实即使指定min-sdk-version为26,通过dx打出来的包,也是不能运行的,因为dx通过同javac一样是通过invokedynamic来调用LambdaMetafactory.metafactory来实现lambda的动态调用,而目前最新的Android版本也没有其具体实现,目前唯一的解决方案就是通过脱糖来支持Lambda表达式。
至于RetroLambda、 Jack、 D8的原理都是一致的,都是生成Labmda接口的实例类型,再通过类型调用具体实现即javac生成的私有静态方法,他们的区别在于脱糖的时机不同:
- RetroLambda通过Transform在javac编译之后,dex编译之前。
- Jack在Jack编译过程中处理输出为dex,不经过javac,直接source to dex
- D8通过D8的编译过程中处理输出为dex,发生在javac之后,具体的gradle task
transformDexArchiveWithDexMergerForDebug生成的dex在build/intermediates/transforms/dexMerger中
Hook Lambda
方法一
Android Studio 3.1默认使用d8 编译器,而脱糖操作在d8当中,无法找到Hook点,所以通过关闭d8的脱糖功能,使用desugarTransform来进行脱糖。
关闭d8脱糖功能,通过 gradle.properties 配置 android.enableD8.desugaring=false 来关闭。
方法二
通过 invokedynamic 指令查找Lambda方法句柄,替换成生成的Hook方法,再在Hook方法中实现 invokedynamic 指令中的代码逻辑。
实现思路:有两种API实现方式Tree API或者Visitor API,Visitor API使用 MethodVisitor 中的 visitInvokeDynamicInsn 方法来实现,Tree API使用 MethodNode 通过访问 instructions 当中的 InvokeDynamicInsnNode。
这里使用Visitor API实现,实例代码如下:
class CustomClassNode(cv: ClassVisitor) : ClassVisitor(Opcodes.ASM7, cv) {
private var className: String? = null
override fun visit(
version: Int,
access: Int,
name: String?,
signature: String?,
superName: String?,
interfaces: Array<out String>?
) {
this.className = name
super.visit(version, access, name, signature, superName, interfaces)
}
override fun visitMethod(
access: Int,
name: String?,
descriptor: String?,
signature: String?,
exceptions: Array<String>?
): MethodVisitor {
return CustomMethodVisitor(
this.className,
cv,
super.visitMethod(access, name, descriptor, signature, exceptions)
)
}
}
class CustomMethodVisitor(private val className: String?, private val cv: ClassVisitor, methodVisitor: MethodVisitor) :
MethodVisitor(Opcodes.ASM7, methodVisitor) {
override fun visitInvokeDynamicInsn(
name: String?,
descriptor: String?,
bootstrapMethodHandle: Handle?,
vararg bootstrapMethodArguments: Any?
) {
val argumentList = bootstrapMethodArguments.toMutableList()
// 函数式接口描述符
val descriptorType: Type = Type.getType(descriptor)
// 方法参数描述符
val methodType: Type? = argumentList[0] as? Type
// 实现方法参数描述符
val methodImplType: Type? = argumentList[2] as? Type
// 方法描述描述符
val methodDesc: String? = methodType?.descriptor
// 方法签名
val methodNameAndDesc: String = name + methodDesc
//---------------------函数式接口过滤-----------------------
// 函数式接口参数描述符
val type: Array<Type> = descriptorType.argumentTypes
val middleMethodDesc = if (type.isEmpty()) {
methodImplType?.descriptor
} else {
"(" + type.joinToString {
it.descriptor
} + methodImplType?.descriptor?.replace("(", "")
}
val middleMethodName = "lambda${'$'}${name}${'$'}trace0"
// invokeDynamic原先的MethodHandle
val oldMethodHandle = argumentList[1] as? Handle
// 要替换的MethodHandle
val newMethodHandle = Handle(Opcodes.H_INVOKESTATIC, className, middleMethodName, middleMethodDesc, false)
argumentList[1] = newMethodHandle
// 生成的中间方法
val methodNode =
MethodNode(Opcodes.ACC_PRIVATE or Opcodes.ACC_STATIC, middleMethodName, middleMethodDesc, null, null)
methodNode.visitCode()
val opcode = when (oldMethodHandle?.tag) {
Opcodes.H_INVOKEINTERFACE -> Opcodes.INVOKEINTERFACE
Opcodes.H_INVOKESPECIAL -> Opcodes.INVOKESPECIAL
Opcodes.H_NEWINVOKESPECIAL -> {
methodNode.visitTypeInsn(Opcodes.NEW, oldMethodHandle.owner)
methodNode.visitInsn(Opcodes.DUP)
Opcodes.INVOKESPECIAL
}
Opcodes.H_INVOKESTATIC -> Opcodes.INVOKESTATIC
Opcodes.H_INVOKEVIRTUAL -> Opcodes.INVOKEVIRTUAL
else -> 0
}
val middleMethodType = Type.getType(middleMethodDesc)
val argumentType = middleMethodType.argumentTypes
if (argumentType.isNotEmpty()) {
var loadIndex = 0
argumentType.forEach {
methodNode.visitVarInsn(it.getOpcode(Opcodes.ILOAD), loadIndex)
loadIndex += it.size
}
}
//---------------------Hook点可插入自定义方法-----------------------
//调用原来的lambda实现
methodNode.visitMethodInsn(opcode, oldMethodHandle?.owner, oldMethodHandle?.name, oldMethodHandle?.desc, false)
val returnType = middleMethodType.returnType
val returnOpcodes = returnType.getOpcode(Opcodes.IRETURN)
methodNode.visitInsn(returnOpcodes)
methodNode.visitEnd()
methodNode.accept(this.cv)
super.visitInvokeDynamicInsn(name, descriptor, bootstrapMethodHandle, *argumentList.toTypedArray())
}
}
反编译原来的字节码和脱糖后的字节码
//源码
public class Java8 {
public Java8() {
}
public static void main(String[] args) {
(new Thread(() -> {
System.out.println("test");
})).start();
}
}
//脱糖后
public class Java8 {
public Java8() {
}
public static void main(String[] args) {
(new Thread(Java8::lambda$run$trace0)).start();
}
private static void lambda$run$trace0() {
lambda$main$0();
}
}