一、背景
Java 8 的 Lambda 表达式已经不再是“新特性”。 现在很多人工作中会使用 Lambda 表达式。 但是,你是否真正理解 Lambda 表达式的底层原理?
本文给出自己的理解,希望对大家有帮助。
二、分析
下面是一段非常简单的代码,其中用到了 Stream
。
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
public class ListDemo {
public static void main(String[] args) {
List<DogDO> dogs = new ArrayList<>();
List<String> tom = dogs.stream().filter(dog -> dog.getName().startsWith("tom")).map(dog -> dog.getName().toLowerCase()).collect(Collectors.toList());
System.out.println(tom);
}
}
我们使用Jclasslib
插件(《那些相见恨晚的 IDEA插件》 中有介绍),查看字节码:
我们可以看到多出一个内部类和 BootstrapMethods
lambdamain0
代码语言:javascript复制0 aload_0
1 invokevirtual #56 <other/list/DogDO.getName : ()Ljava/lang/String;>
4 ldc #67 <tom>
6 invokevirtual #69 <java/lang/String.startsWith : (Ljava/lang/String;)Z>
9 ireturn
相当于:
代码语言:javascript复制private static Boolean lambda$main$0(DogDO dogDo){
return dogDo.getName().startsWith("tom");
}
lambdamain1
代码语言:javascript复制0 aload_0
1 invokevirtual #56 <other/list/DogDO.getName : ()Ljava/lang/String;>
4 invokevirtual #62 <java/lang/String.toLowerCase : ()Ljava/lang/String;>
7 areturn
相当于
代码语言:javascript复制private static String lambda$main$1(DogDO dogDo){
return dogDo.getName().toLowerCase();
}
通过上述简单分析就可以看出来,本质上 lambda 表达式最终会被编译为私有静态方法。
main 方法
代码语言:javascript复制 0 new #7 <java/util/ArrayList>
3 dup
4 invokespecial #9 <java/util/ArrayList.<init> : ()V>
7 astore_1
8 aload_1
9 invokeinterface #10 <java/util/List.stream : ()Ljava/util/stream/Stream;> count 1
14 invokedynamic #16 <test, BootstrapMethods #0>
19 invokeinterface #20 <java/util/stream/Stream.filter : (Ljava/util/function/Predicate;)Ljava/util/stream/Stream;> count 2
24 invokedynamic #26 <apply, BootstrapMethods #1>
29 invokeinterface #30 <java/util/stream/Stream.map : (Ljava/util/function/Function;)Ljava/util/stream/Stream;> count 2
34 invokestatic #34 <java/util/stream/Collectors.toList : ()Ljava/util/stream/Collector;>
37 invokeinterface #40 <java/util/stream/Stream.collect : (Ljava/util/stream/Collector;)Ljava/lang/Object;> count 2
42 checkcast #11 <java/util/List>
45 astore_2
46 getstatic #44 <java/lang/System.out : Ljava/io/PrintStream;>
49 aload_2
50 invokevirtual #50 <java/io/PrintStream.println : (Ljava/lang/Object;)V>
53 return
通过 invokedynamic 指令执行动态方法调用。
如
代码语言:javascript复制14 invokedynamic #16 <test, BootstrapMethods #0>
可以在插件里一直跟下去
其中 BootstrapMethods # 0
对应
可以看到这是对 java.lang.invoke.LambdaMetafactory#metafactory
的调用,返回值是 java.lang.invoke.CallSite 对象,这个对象代表了真正执行的目标方法调用。
/**
* 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 bootstrap method for {@code invokedynamic}
* call sites, to support the lambda expression and method
* reference expression features of the Java Programming Language.
*
* 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}.
*
* 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. Specifically, the lookup context
* must have
* private access
* privileges.
* 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, or the lookup context
* does not have private access privileges.
*/
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();
}
即 lambda 表达式将代码写到私有静态方法中,然后构造目标类型的实现。
逻辑层面等价于下面代码:
代码语言:javascript复制package other.list;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;
public class ListDemo {
public static void main(String[] args) {
List<DogDO> dogs = new ArrayList<>();
List<String> tom = dogs.stream().filter(new Predicate<DogDO>() {
@Override
public boolean test(DogDO dogDO) {
return lambda$main$0(dogDO);
}
}).map(new Function<DogDO, String>() {
@Override
public String apply(DogDO dogDO) {
return lambda$main$1(dogDO);
}
}).collect(Collectors.toList());
System.out.println(tom);
}
private static Boolean lambda$min$0(DogDO dogDo) {
return dogDo.getName().startsWith("tom");
}
private static String lambda$main$1(DogDO dogDo) {
return dogDo.getName().toLowerCase();
}
}
本文只是抛砖引玉,大家还可以继续深入研究。
三、总结
很多知识看似习以为常,但是都可以继续深挖,可以学到不一样的知识。
创作不易,如果本文对你有帮助,你的支持和鼓励,是我创作的最大动力。