Lambda 表达式详解 1. Lambda 表达式的形式
无参数
1 2 3 4 5 6 7 NoArgument noArgument = () -> { System.out.println("No Argument" ); }; public interface NoArgument { void noArg () ; }
一个参数
1 2 3 4 5 6 7 OneArgument oneArgument = (a) -> { System.out.println("One Argument:" + a); }; public interface OneArgument { void oneArg (int one) ; }
多个参数
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) ; }
当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>
二元操作,其中T
和U
是输入参数类型,R
输出参数类型
上式表达的意思是,将Float a
与Integer 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 示例
当有 @FunctionalInterface
修饰的接口中包含多个方法时:
当有 @FunctionalInterface
修饰的接口中,没有必须实现的方法时,即全部都有默认实现:
普通类可实现有 @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; } }
对于只有一个方法的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表达式在调用过程中有以下特点:
lambda表达式引用的是值,而不是变量。
lambda表达式的类型依赖与上下文,经由编译器推断得到具体类型。
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 5 : astore_1 6 : getstatic #3 9 : aload_1 10 : bipush 10 12 : invokestatic #4 15 : invokeinterface #5 , 2 20 : invokevirtual #6 23 : new #7 26 : dup 27 : invokespecial #8 30 : new #9 33 : dup 34 : invokespecial #10 37 : invokevirtual #11 40 : new #7 43 : dup 44 : invokespecial #8 47 : getstatic #3 50 : dup 51 : invokevirtual #12 54 : pop 55 : invokedynamic #13 , 0 60 : invokevirtual #11 63 : new #7 66 : dup 67 : invokespecial #8 70 : invokedynamic #14 , 0 75 : invokevirtual #15 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 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
指令:
虽然在写法上,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
来生成CallSite
,CallSite
可以被视为函数对象的”工厂”,用于创建合适的功能对象。当接口继承了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 ) { 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 { 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); 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(); } MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, samMethodName, samMethodType.toMethodDescriptorString(), null , null ); mv.visitAnnotation("Ljava/lang/invoke/LambdaForm$Hidden;" , true ); new ForwardingMethodGenerator (mv).generate(samMethodType); 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(); final byte [] classBytes = cw.toByteArray(); 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" ), 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 /Usersout/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 #2 = InvokeDynamic #0 :#89 #3 = Fieldref #90. #91 #4 = Methodref #92. #93 #5 = InterfaceMethodref #40. #94 #6 = Methodref #95. #96 #7 = Class #97 #8 = Methodref #7. #83 #9 = Class #98 #10 = Methodref #9. #83 #11 = Methodref #7. #99 #12 = Methodref #39. #100 #13 = InvokeDynamic #1 :#103 #14 = InvokeDynamic #2 :#109 #15 = Methodref #7. #110 #16 = InterfaceMethodref #45. #111 #17 = InterfaceMethodref #43. #112 #18 = Methodref #113. #114 #19 = Methodref #115. #116 #20 = String #76 #21 = Methodref #115. #117 #22 = Methodref #113. #118 #23 = Methodref #113. #119 #24 = String #120 #25 = Methodref #39. #117 #26 = Methodref #113. #121 #27 = String #122 #28 = Methodref #113. #123 #29 = String #48 #30 = Methodref #113. #124 #31 = String #97 #32 = Methodref #113. #125 #33 = Class #126 #34 = String #127 #35 = Methodref #33. #128 #36 = String #129 #37 = Methodref #95. #130 #38 = Methodref #92. #131 #39 = Class #132 #40 = Class #133 #41 = Utf8 TestFun #42 = Utf8 InnerClasses #43 = Class #120 #44 = Utf8 NoArgument #45 = Class #134 #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 #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 #84 = Utf8 BootstrapMethods #85 = MethodHandle #6 :#136 #86 = MethodType #137 #87 = MethodHandle #6 :#138 #88 = MethodType #78 #89 = NameAndType #62 :#139 #90 = Class #140 #91 = NameAndType #141 :#142 #92 = Class #143 #93 = NameAndType #144 :#145 #94 = NameAndType #62 :#137 #95 = Class #146 #96 = NameAndType #147 :#148 #97 = Utf8 cn/cwiki/java8/TestLambda #98 = Utf8 cn/cwiki/java8/TestLambda$1 #99 = NameAndType #62 :#63 #100 = NameAndType #149 :#150 #101 = MethodType #151 #102 = MethodHandle #5 :#152 #103 = NameAndType #153 :#154 #104 = MethodHandle #6 :#155 #105 = MethodType #48 #106 = MethodHandle #6 :#156 #107 = Integer 5 #108 = Integer 0 #109 = NameAndType #122 :#157 #110 = NameAndType #66 :#67 #111 = NameAndType #153 :#151 #112 = NameAndType #122 :#48 #113 = Class #158 #114 = NameAndType #159 :#160 #115 = Class #135 #116 = NameAndType #161 :#162 #117 = NameAndType #163 :#164 #118 = NameAndType #165 :#162 #119 = NameAndType #166 :#160 #120 = Utf8 cn/cwiki/java8/TestLambda$NoArgument #121 = NameAndType #167 :#160 #122 = Utf8 noArg #123 = NameAndType #168 :#160 #124 = NameAndType #169 :#160 #125 = NameAndType #170 :#160 #126 = Utf8 java/lang/IllegalArgumentException #127 = Utf8 Invalid lambda deserialization #128 = NameAndType #47 :#171 #129 = Utf8 Serializable #130 = NameAndType #147 :#171 #131 = NameAndType #172 :#162 #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 #137 = Utf8 (Ljava/lang/Object;)Ljava/lang/Object; #138 = Methodref #7. #175 #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 #153 = Utf8 oneArg #154 = Utf8 (Ljava/io/PrintStream;)Lcn/cwiki/java8/TestLambda$OneArgument; #155 = Methodref #173. #177 #156 = Methodref #7. #178 #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 #174 = NameAndType #180 :#183 #175 = NameAndType #77 :#78 #176 = NameAndType #147 :#151 #177 = NameAndType #184 :#185 #178 = NameAndType #76 :#48 #179 = Utf8 java/lang/invoke/LambdaMetafactory #180 = Utf8 metafactory #181 = Class #187 #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 #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 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 5 : astore_1 6 : getstatic #3 9 : aload_1 10 : bipush 10 12 : invokestatic #4 15 : invokeinterface #5 , 2 20 : invokevirtual #6 23 : new #7 26 : dup 27 : invokespecial #8 30 : new #9 33 : dup 34 : invokespecial #10 37 : invokevirtual #11 40 : new #7 43 : dup 44 : invokespecial #8 47 : getstatic #3 50 : dup 51 : invokevirtual #12 54 : pop 55 : invokedynamic #13 , 0 60 : invokevirtual #11 63 : new #7 66 : dup 67 : invokespecial #8 70 : invokedynamic #14 , 0 75 : invokevirtual #15 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 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 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 ; public static #44 = #43 of #7 ; public static #46 = #45 of #7 ; static #9 ; public static final #182 = #181 of #186 ; 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 调用接口方法
引用