Lambda表达式初体验
简介
Lambda 表达式(lambda expression)是一个匿名函数,Lambda表达式基于数学中的λ演算得名,直接对应于其中的lambda抽象(lambda abstraction),是一个匿名函数,即没有函数名的函数。Lambda表达式可以表示闭包(注意和数学传统意义上的不同)。
——《百度百科》
Java
对于Lambda表达式的支持是从JDK8开始的,它来源于数学中的λ演算,是一套关于函数\\(f(x)\\)定义、输入量、输出量的计算方案,简化了匿名函数的编写,使代码变得简洁。
另外,提到Lambda表达式,就不得不提及函数式编程。
函数式编程
函数式编程,或称函数程序设计、泛函编程(英语:Functional programming),是一种编程范式,它将电脑运算视为函数运算,并且避免使用程序状态以及易变对象。其中,λ演算为该语言最重要的基础。而且,λ演算的函数可以接受函数作为输入参数和输出返回值。
比起指令式编程,函数式编程更加强调程序执行的结果而非执行的过程,倡导利用若干简单的执行单元让计算结果不断渐进,逐层推导复杂的运算,而不是设计一个复杂的执行过程。
在函数式编程中,函数是头等对象,意思是说一个函数,既可以作为其它函数的输入参数值,也可以从函数中返回值,被修改或者被分配给一个变量。——《维基百科》
总结函数式编程的特点:
- 函数是“头等公民”
- 可以赋值给变量
- 可以作为其它函数的参数进行传递
- 可以作为其它函数的返回值
接下来用一个例子感受一下什么是Lambda表达式。
举个例子
首先声明一个接口Factory
和一个User
类。
public interface Factory {
Object getObject();
}
public class User {
private String name;
private int age;
public User() {}
public User(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return \"User{\" +
\"name=\'\" + name + \'\\\'\' +
\", age=\" + age +
\'}\';
}
}
按照传统的方式,想要实现Factory
接口中的抽象方法,有两种方法:
- 子类实现接口
- 匿名内部类
对于第一种方式,代码如下:
public class SubClass implements Factory {
@Override
public Object getObject() {
return new User(\"tom\", 20);
}
}
public class Code01_LambdaTest {
public static void main(String[] args) {
// 子类实现接口
Factory factory = new SubClass();
System.out.println(\"user = \" + user);
}
}
对于第二种方式,代码如下:
public class Code01_LambdaTest {
public static void main(String[] args) {
// 匿名内部类
Factory factory = new Factory() {
@Override
public Object getObject() {
return new User(\"John\", 18);
}
};
System.out.println(\"user = \" + user);
}
}
分析上面的代码,发现接口Factory
中仅仅只有一个抽象方法,我们的目的是为了获取User
类的对象这么一个简单的操作,却需要书写这么多代码,最最核心的其实就是这一句return new User(\"John\", 18)
,有没有更加简洁的书写方式实现上面的需求呢?
第三种方法,就是我们要介绍的Lambda表达式
。请看代码:
public class Code01_LambdaTest {
public static void main(String[] args) {
// lambda表达式
Factory factory = () -> new User(\"Mike\", 30);
System.out.println(\"user = \" + user);
}
}
通过对比就会发现,代码是多么的简洁高效!
Lambda表达式的语法格式
使用前提
必须要有一个函数式接口
,有且仅有一个抽象方法的接口,要添加注解@FunctionalInterface
!
两种语法格式
(parameters) -> {statements}
(patameters) -> expression
说明如下:
parameters
是函数的参数列表
当参数不止一个时,()
不可省略,否则可以省略()
和类型;
当函数式接口中抽象方法的参数列表的参数类型可以自动推断时,可以省略对应的类型statements
是执行语句
当函数体仅有一个语句,可以省略{}
,否则不可省略;expression
是表达式
当函数体只有一个表达式,且运算结果匹配返回值类型,可以省略return
关键字->
是使用指定参数去完成某个功能,不可省略
常见的函数式接口
- Runnable、Callable
- Supplier、Consumer
- Comparator
- Predicate
- Function
Lambda表达式应用举例
注意,以下源码是基于Java17的。
Runnable、Callable接口
接口介绍
Java-多线程:Callable接口和Runnable接口之间的区别
接口源码
package java.lang;
@FunctionalInterface
public interface Runnable {
public abstract void run();
}
package java.util.concurrent;
@FunctionalInterface
public interface Callable<V> {
V call() throws Exception;
}
案例
public static void main(String[] args) {
// 使用匿名内部类的方式实现多线程
new Thread(new Runnable() {
@Override
public void run() {
String name = Thread.currentThread().getName();
System.out.println(\"线程 \" + name + \" 已启动!\");
}
}).start();
// 使用Lambda表达式
new Thread(() -> {
String name = Thread.currentThread().getName();
System.out.println(\"Lambda::线程 \" + name + \" 已启动!\");
}).start();
}
Supplier、Consumer接口
接口介绍
Supplier
接口是一个供给型的接口,需要实现get
方法。它更像是一个容器,可以用来存储数据,然后可以供其他方法使用。
Consumer
接口就是一个消费型的接口,通过传入参数,然后输出值。Consumer
是一个接口,并且只要实现一个accept
方法,就可以作为一个消费者输出信息。andThen
方法的返回值仍为Consumer
接口,因此可以持续消费。
接口源码
package java.util.function;
@FunctionalInterface
public interface Supplier<T> {
T get();
}
package java.util.function;
import java.util.Objects;
@FunctionalInterface
public interface Consumer<T> {
void accept(T t);
default Consumer<T> andThen(Consumer<? super T> after) {
Objects.requireNonNull(after);
return (T t) -> { accept(t); after.accept(t); };
}
}
案例
对于Supplier
供应商接口,我们求数组arr
中的最大值。
public static void main(String[] args) {
int[] arr = {1, 5, 7, 9, 2, 4, 6, 8};
// 获取数组的最大值
Supplier<Integer> supplier = () -> {
int max = Integer.MIN_VALUE;
for (int j : arr) {
if (j > max) max = j;
}
return max;
};
System.out.println(supplier.get());
}
对于Consumer
消费者接口,我们测试持续消费。
public static void consumer(Consumer<String> first, Consumer<String> sec) {
first.andThen(sec).accept(\"Tt\");
}
然后在main
方法中,先消费一条消息\"hello\"
,接着再消费一条消息\"Tt\"
public static void main(String[] args) {
Consumer<String> consumer = msg -> System.out.println(\"msg = \" + msg);
consumer.accept(\"hello\");
consumer(
consumer,
s -> System.out.println(s.toUpperCase())
);
}
程序的运行结果为:
msg = hello
msg = Tt
TT
Comparator接口
接口介绍
Comparator
接口是一个专用的比较器,当这个对象不支持自比较或者自比较函数不能满足要求时,可写一个比较器来完成两个对象之间大小的比较。Comparator体现了一种策略模式
(strategy design pattern),就是不改变对象自身,而用一个策略对象(strategy object)来改变它的行为。
强行对某个对象collection进行整体排序的比较函数。可以将 Comparator 传递给 sort 方法(如 Collections.sort 或 Arrays.sort),从而允许在排序顺序上实现精确控制。还可以使用 Comparator 来控制某些数据结构(如有序 set 或有序映射)的顺序,或者为那些没有自然顺序的对象 collection 提供排序。
当且仅当对于一组元素 S 中的每个 e1 和 e2 而言,c.compare(e1, e2)==0 与 e1.equals(e2) 具有相等的布尔值时,Comparator c 强行对 S 进行的排序才叫做与 equals 一致 的排序。
当使用具有与 equals 不一致的强行排序能力的 Comparator 对有序 set(或有序映射)进行排序时,应该小心谨慎。假定一个带显式 Comparator c 的有序 set(或有序映射)与从 set S 中抽取出来的元素(或键)一起使用。如果 c 强行对 S 进行的排序是与 equals 不一致的,那么有序 set(或有序映射)将是行为“怪异的”。尤其是有序 set(或有序映射)将违背根据 equals 所定义的 set(或映射)的常规协定。
例如,假定使用 Comparator c 将满足 (a.equals(b) && c.compare(a, b) != 0) 的两个元素 a 和 b 添加到一个空 TreeSet 中,则第二个 add 操作将返回 true(树 set 的大小将会增加),因为从树 set 的角度来看,a 和 b 是不相等的,即使这与 Set.add 方法的规范相反。
注:通常来说,让Comparator也实现 java.io.Serializable 是一个好主意,因为它们在可序列化的数据结构(像 TreeSet 、TreeMap)中可用作排序方法。为了成功地序列化数据结构,Comparator(如果已提供)必须实现Serializable。——《官方文档》
这里不得不提Comparable
接口:
此接口强行对实现它的每个类的对象进行整体排序。这种排序被称为类的自然排序,类的compareTo方法被称为它的自然比较方法。
实现此接口的对象列表(和数组)可以通过Collections.sort(和Arrays.sort)进行自动排序。实现此接口的可以用作有序映射(实现了SortedMap接口的对象)或有序集合(实现了SortedSet接口的对象)中的元素,无需指定比较器。
建议(虽然不是必需的)最好使自然排序与equals一致。所谓自然排序与equals一致指的是 类A 对于每一个 o1 和 o2 来说,当且仅当 ( o1.compareTo( o2 ) )与 o1.equals( o2 )具有相同的 布尔值 时,类A的自然排序才叫做与equals一致。——《官方文档》
两个接口有什么区别?
- Comparator位于包java.util下,而Comparable位于包java.lang下。
- Comparable接口将比较代码写入需要进行比较类的代码中,而Comparator接口在一个独立的类中实现比较。
- Comparator接口相对更灵活,因为它跟接口实现的类是耦合在一起的,可以通过更换比较器来改变不同的比较规则。
- Comparable接口强制进行自然排序,而Comparator接口不强制进行自然排序,可以指定排序顺序。
接口源码
package java.util;
import java.io.Serializable;
import java.util.function.Function;
import java.util.function.ToIntFunction;
import java.util.function.ToLongFunction;
import java.util.function.ToDoubleFunction;
import java.util.Comparators;
@FunctionalInterface
public interface Comparator<T> {
int compare(T o1, T o2);
boolean equals(Object obj);
default Comparator<T> reversed() {
return Collections.reverseOrder(this);
}
default Comparator<T> thenComparing(Comparator<? super T> other) {
Objects.requireNonNull(other);
return (Comparator<T> & Serializable) (c1, c2) -> {
int res = compare(c1, c2);
return (res != 0) ? res : other.compare(c1, c2);
};
}
...
}
注意:boolean equals(Object obj);
是属于父类Object
的,因此它还是函数式接口,仍然只有一个抽象方法int compare(T o1, T o2);
。
案例
public static void main(String[] args) {
String[] arr = {\"ab\", \"c\", \"d\", \"go\", \"bee\"};
Comparator<String> comparator = String::compareTo;
comparator = Comparator.reverseOrder();
comparator = (o1, o2) -> o2.length() - o1.length();
Arrays.sort(arr, comparator);
System.out.println(\"arr = \" + Arrays.toString(arr));
}
依次运行的结果:
arr = [ab, bee, c, d, go]
arr = [go, d, c, bee, ab]
arr = [bee, go, ab, d, c]
Predicate接口
接口介绍
Predicate
接口主要用于流的筛选。给定一个包含若干项的流,Stream 接口的filter
方法传入Predicate
并返回一个新的流,它仅包含满足给定谓词的项。可以使用 lambda 表达式或方法引用来实现boolean test(T t)
方法。
接口源码
package java.util.function;
import java.util.Objects;
@FunctionalInterface
public interface Predicate<T> {
boolean test(T t);
default Predicate<T> and(Predicate<? super T> other) {
Objects.requireNonNull(other);
return (t) -> test(t) && other.test(t);
}
default Predicate<T> negate() {
return (t) -> !test(t);
}
default Predicate<T> or(Predicate<? super T> other) {
Objects.requireNonNull(other);
return (t) -> test(t) || other.test(t);
}
static <T> Predicate<T> isEqual(Object targetRef) {
return (null == targetRef)
? Objects::isNull
: object -> targetRef.equals(object);
}
static <T> Predicate<T> not(Predicate<? super T> target) {
Objects.requireNonNull(target);
return (Predicate<T>)target.negate();
}
}
注意:Predicate
接口包含的单一抽象方法为boolean test(T t)
,它传入一个泛型参数并返回true
或 false
。
案例
给定一个字符串String str = \"Hello the world !\";
判断是否包含指定的字符。
定义3个方法,分别实现与
、或
、非
的功能。
public static boolean and(Predicate<String> p1, Predicate<String> p2, String s) {
return p1.and(p2).test(s);
}
public static boolean or(Predicate<String> p1, Predicate<String> p2, String s) {
return p1.or(p2).test(s);
}
public static boolean negate(Predicate<String> p, String s) {
return p.negate().test(s);
}
然后在main
方法中调用
public static void main(String[] args) {
String str = \"Hello the world !\";
boolean ans;
ans = and(s -> s.contains(\"H\"), s -> s.contains(\"w\"), str);
ans = or(s -> s.contains(\"A\"), s -> s.contains(\"z\"), str);
ans = negate(s -> s.length() < 10, str);
System.out.println(\"ans = \" + ans);
}
逐一运行,运行结果为:
ans = true
ans = false
ans = true
Function接口
接口介绍
函数式接口(Functional Interface)有且仅有一个抽象方法,但是可以有多个非抽象方法的接口。函数式接口可以被隐式转换为 Lambda 表达式。
Java 8 中提供了一个函数式接口 Function
,这个接口表示对一个参数做一些操作然后返回操作之后的值。这个接口的有一个抽象方法 apply
,这个方法就是表明对参数做的操作。
接口源码
package java.util.function;
import java.util.Objects;
@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;
}
}
注意:
- 这里的抽象方法是
apply
,可以从数学上理解Function
接口,即\\(f:T \\rightarrow R\\),输入一个\\(T\\)可以返回一个\\(R\\),\\(f\\)就是定义的一套规则,交由apply
执行。 - 对于
andThen
方法,它返回一个组合函数,一个函数的输出将作为另一个函数的输入。如果对任一函数的求值引发异常,则将异常抛给组合函数的调用方。
案例
接下来用对多项式进行求导的例子进行演示。
对于多项式\\(f(x) = 5x^3 + 2x^2 + x + 6\\),定义数组C=[5,2,1,6]
表示它的系数。
首先定义一个用于求导数的lambda表达式:
Function<Integer[], Integer[]> d = x -> {
if (x.length == 1) {
return new Integer[]{0};
}
Integer[] C1 = new Integer[x.length - 1];
for (int i = 0; i < x.length - 1; i++) {
C1[i] = x[i] * (x.length - 1 - i);
}
return C1;
};
为了方便查看,定义一个打印多项式的方法:
public static void showX(Integer[] C) {
if (C.length == 1) {
System.out.println(\"f(x) = \" + C[0]);
return;
}
StringBuilder s = new StringBuilder();
int size = C.length - 1;
for (int i = 0; i <= size; i++) {
int pow = size - i;
if (C[i] != 0 && pow > 0) {
if (C[i] != 1) s.append(C[i]);
if (pow == 1) s.append(\"x\");
else s.append(\"x^\").append(pow);
s.append(\" + \");
}
if (C[i] != 0 && pow == 0) s.append(C[i]);
}
System.out.println(\"f(x) = \" + s);
}
接下来就可以在main
方法里进行调用了,我们可以借助Function
接口中的apply
方法对多项式进行多次求导。
public static void main(String[] args) {
// 求多项式二阶导数
// f(x) = 5x^3 + 2x^2 + x + 6
// 系数 = [5,2,1,6]
// 幂次 = [3,2,1,0]
// 预期系数 = [30,4]
// 预期幂次 = [1,0]
Integer[] C = {5, 2, 1, 6};
showX(C);
// 求一阶导数
Integer[] C1 = d.apply(C);
showX(C1);
// 求二阶导数
Integer[] C2 = d.andThen(d).apply(C);
showX(C2);
// 求三阶导数
Integer[] C3 = d.andThen(d.andThen(d)).apply(C);
showX(C3);
}
执行结果:
f(x) = 5x^3 + 2x^2 + x + 6
f(x) = 15x^2 + 4x + 1
f(x) = 30x + 4
f(x) = 30
Lambda的底层实现原理剖析
Lambda表达式的本质
它的本质是函数式接口的匿名子类的匿名对象。底层是依赖ASM技术、由匿名内部类实现的,由于Java是面向对象的语言,因此Lambda表达式是一个语法糖,它不能直接执行,需要转换成内部类才可以运行。
编译和运行的过程图
以下面的代码举例:
import java.util.Arrays;
import java.util.List;
public class LambdaPrinciple {
public static void main(String[] args) {
List<String> list = Arrays.asList(\"I\", \"Love\", \"You\");
list.forEach(s -> System.out.println(s));
}
}
上述代码的编译和运行过程如图1所示:
详细过程分析
通过反编译得到字节码文件,可以分析Lambda表达式的本质。将cfr-0.145.jar
包添加到LambdaPrinciple
类的编译路径,如图2所示。
执行反编译命令:java -jar cfr-0.145.jar LambdaPrinciple.class --decodelambdas false
,如图3所示。
得到反编译的LambdaPrinciple.class
import java.io.PrintStream;
import java.lang.invoke.LambdaMetafactory;
import java.util.Arrays;
import java.util.List;
import java.util.function.Consumer;
public class LambdaPrinciple {
public static void main(String[] args) {
List<String> list = Arrays.asList(\"I\", \"Love\", \"You\");
list.forEach((Consumer<String>) LambdaMetafactory.metafactory(
null, null, null,
(Ljava / lang / Object;)V,
lambda$main$0(java.lang.String),
(Ljava / lang / String;)V)());
}
private static /* synthetic */ void lambda$main$0(String s) {
System.out.println(s);
}
}
从上面可以看到,Lambda表达式最终被编译成lambda$main$0
静态方法去执行。
接下来找到LambdaMetafactory.java
,看下它的metafactory
方法
public final class LambdaMetafactory {
...
// LambdaMetafactory bootstrap methods are startup sensitive, and may be
// special cased in java.lang.invoke.BootstrapMethodInvoker to ensure
// methods are invoked with exact type information to avoid generating
// code for runtime checks. Take care any changes or additions here are
// reflected there as appropriate.
/**
* 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 factoryType},
* declares a method with the name given by {@code interfaceMethodName} and the
* signature given by {@code interfaceMethodType}. 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 {@linkplain MethodHandles.Lookup#hasFullPrivilegeAccess()
* full privilege access}.
* When used with {@code invokedynamic}, this is stacked
* automatically by the VM.
* @param interfaceMethodName 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 factoryType 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.
* @param interfaceMethodType Signature and return type of method to be
* implemented by the function object.
* @param implementation A direct method handle describing the implementation
* method which should be called (with suitable adaptation
* of argument types and return types, and with captured
* arguments prepended to the invocation arguments) at
* invocation time.
* @param dynamicMethodType The signature and return type that should
* be enforced dynamically at invocation time.
* In simple use cases this is the same as
* {@code interfaceMethodType}.
* @return a CallSite whose target can be used to perform capture, generating
* instances of the interface named by {@code factoryType}
* @throws LambdaConversionException If {@code caller} does not have full privilege
* access, or if {@code interfaceMethodName} is not a valid JVM
* method name, or if the return type of {@code factoryType} is not
* an interface, or if {@code implementation} is not a direct method
* handle referencing a method or constructor, or if the linkage
* invariants are violated, as defined {@link LambdaMetafactory above}.
* @throws NullPointerException If any argument is {@code null}.
* @throws SecurityException If a security manager is present, and it
* <a href=\"MethodHandles.Lookup.html#secmgr\">refuses access</a>
* from {@code caller} to the package of {@code implementation}.
*/
public static CallSite metafactory(MethodHandles.Lookup caller,
String interfaceMethodName,
MethodType factoryType,
MethodType interfaceMethodType,
MethodHandle implementation,
MethodType dynamicMethodType)
throws LambdaConversionException {
AbstractValidatingLambdaMetafactory mf;
mf = new InnerClassLambdaMetafactory(Objects.requireNonNull(caller),
Objects.requireNonNull(factoryType),
Objects.requireNonNull(interfaceMethodName),
Objects.requireNonNull(interfaceMethodType),
Objects.requireNonNull(implementation),
Objects.requireNonNull(dynamicMethodType),
false,
EMPTY_CLASS_ARRAY,
EMPTY_MT_ARRAY);
mf.validateMetafactoryArgs();
return mf.buildCallSite();
}
}
接下来看下new InnerClassLambdaMetafactory()
的代码,它的作用是创建内部类
public InnerClassLambdaMetafactory(MethodHandles.Lookup caller,
MethodType factoryType,
String interfaceMethodName,
MethodType interfaceMethodType,
MethodHandle implementation,
MethodType dynamicMethodType,
boolean isSerializable,
Class<?>[] altInterfaces,
MethodType[] altMethods)
throws LambdaConversionException {
super(caller, factoryType, interfaceMethodName, interfaceMethodType,
implementation, dynamicMethodType,
isSerializable, altInterfaces, altMethods);
implMethodClassName = implClass.getName().replace(\'.\', \'/\');
implMethodName = implInfo.getName();
implMethodDesc = implInfo.getMethodType().toMethodDescriptorString();
constructorType = factoryType.changeReturnType(Void.TYPE);
lambdaClassName = lambdaClassName(targetClass);
// If the target class invokes a protected method inherited from a
// superclass in a different package, or does \'invokespecial\', the
// lambda class has no access to the resolved method. Instead, we need
// to pass the live implementation method handle to the proxy class
// to invoke directly. (javac prefers to avoid this situation by
// generating bridges in the target class)
useImplMethodHandle = (Modifier.isProtected(implInfo.getModifiers()) &&
!VerifyAccess.isSamePackage(targetClass, implInfo.getDeclaringClass())) ||
implKind == H_INVOKESPECIAL;
cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
int parameterCount = factoryType.parameterCount();
if (parameterCount > 0) {
argNames = new String[parameterCount];
argDescs = new String[parameterCount];
for (int i = 0; i < parameterCount; i++) {
argNames[i] = \"arg$\" + (i + 1);
argDescs[i] = BytecodeDescriptor.unparse(factoryType.parameterType(i));
}
} else {
argNames = argDescs = EMPTY_STRING_ARRAY;
}
}
值得注意的是cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
,它的作用是构造一个新的ClassWriter对象,写字节码文件,这其实就是ASM技术。
回到LambdaMetafactory.java
,查看metafactory
方法它的返回值,走到这一步时,其实准备工作已经完成了,关键的代码是final Class<?> innerClass = spinInnerClass();
要返回内部类的字节码文件。
@Override
CallSite buildCallSite() throws LambdaConversionException {
final Class<?> innerClass = spinInnerClass();
if (factoryType.parameterCount() == 0) {
// In the case of a non-capturing lambda, we optimize linkage by pre-computing a single instance,
// unless we\'ve suppressed eager initialization
if (disableEagerInitialization) {
try {
return new ConstantCallSite(caller.findStaticGetter(innerClass, LAMBDA_INSTANCE_FIELD,
factoryType.returnType()));
} catch (ReflectiveOperationException e) {
throw new LambdaConversionException(
\"Exception finding \" + LAMBDA_INSTANCE_FIELD + \" static field\", e);
}
} else {
@SuppressWarnings(\"removal\")
final Constructor<?>[] ctrs = AccessController.doPrivileged(
new PrivilegedAction<>() {
@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(interfaceClass, inst));
} catch (ReflectiveOperationException e) {
throw new LambdaConversionException(\"Exception instantiating lambda object\", e);
}
}
} else {
try {
MethodHandle mh = caller.findConstructor(innerClass, constructorType);
return new ConstantCallSite(mh.asType(factoryType));
} catch (ReflectiveOperationException e) {
throw new LambdaConversionException(\"Exception finding constructor\", e);
}
}
}
接下来查看spinInnerClass
的源码
private Class<?> spinInnerClass() throws LambdaConversionException {
// CDS does not handle disableEagerInitialization.
if (!disableEagerInitialization) {
// include lambda proxy class in CDS archive at dump time
if (CDS.isDumpingArchive()) {
Class<?> innerClass = generateInnerClass();
LambdaProxyClassArchive.register(targetClass,
interfaceMethodName,
factoryType,
interfaceMethodType,
implementation,
dynamicMethodType,
isSerializable,
altInterfaces,
altMethods,
innerClass);
return innerClass;
}
// load from CDS archive if present
Class<?> innerClass = LambdaProxyClassArchive.find(targetClass,
interfaceMethodName,
factoryType,
interfaceMethodType,
implementation,
dynamicMethodType,
isSerializable,
altInterfaces,
altMethods);
if (innerClass != null) return innerClass;
}
return generateInnerClass();
}
接下来查看generateInnerClass()
的源码
private Class<?> generateInnerClass() throws LambdaConversionException {
String[] interfaceNames;
String interfaceName = interfaceClass.getName().replace(\'.\', \'/\');
boolean accidentallySerializable = !isSerializable && Serializable.class.isAssignableFrom(interfaceClass);
if (altInterfaces.length == 0) {
interfaceNames = new String[]{interfaceName};
} else {
// Assure no duplicate interfaces (ClassFormatError)
Set<String> itfs = new LinkedHashSet<>(altInterfaces.length + 1);
itfs.add(interfaceName);
for (Class<?> i : altInterfaces) {
itfs.add(i.getName().replace(\'.\', \'/\'));
accidentallySerializable |= !isSerializable && Serializable.class.isAssignableFrom(i);
}
interfaceNames = itfs.toArray(new String[itfs.size()]);
}
cw.visit(CLASSFILE_VERSION, ACC_SUPER + ACC_FINAL + ACC_SYNTHETIC,
lambdaClassName, null,
JAVA_LANG_OBJECT, interfaceNames);
// 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 (factoryType.parameterCount() == 0 && disableEagerInitialization) {
generateClassInitializer();
}
// Forward the SAM method
MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, interfaceMethodName,
interfaceMethodType.toMethodDescriptorString(), null, null);
new ForwardingMethodGenerator(mv).generate(interfaceMethodType);
// Forward the altMethods
if (altMethods != null) {
for (MethodType mt : altMethods) {
mv = cw.visitMethod(ACC_PUBLIC, interfaceMethodName,
mt.toMethodDescriptorString(), null, null);
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<>() {
@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\"));
}
try {
// this class is linked at the indy callsite; so define a hidden nestmate
Lookup lookup;
if (useImplMethodHandle) {
lookup = caller.defineHiddenClassWithClassData(classBytes, implementation, !disableEagerInitialization,
NESTMATE, STRONG);
} else {
lookup = caller.defineHiddenClass(classBytes, !disableEagerInitialization, NESTMATE, STRONG);
}
return lookup.lookupClass();
} catch (IllegalAccessException e) {
throw new LambdaConversionException(\"Exception defining lambda proxy class\", e);
} catch (Throwable t) {
throw new InternalError(t);
}
}
这里要注意的是dumper
,如果请求,转储到文件以进行调试,点进去,可以得到下面的JVM参数:final String dumpProxyClassesKey = \"jdk.internal.lambda.dumpProxyClasses\";
。
然后在LambdaPrinciple
字节码文件所在的路径,继续反编译,将内部类转储出来。
转储命令为:java -Djdk.internal.lambda.dumpProxyClasses LambdaPrinciple
,得到LambdaPrinciple$$Lambda$1.class
。
然后再将其进行反编译java -jar cfr-0.145.jar LambdaPrinciple$$Lambda$1.class --decodelambdas false
,得到如下的结果:
final class LambdaPrinciple$$Lambda$1 implements Consumer {
private LambdaPrinciple$$Lambda$1() {
}
@LambdaForm.Hidden
public void accept(Object object) {
LambdaPrinciple.lambda$main$0((String)object);
}
}
整合上面反编译的代码,如下所示:
public class LambdaPrinciple {
final class LambdaPrinciple$$Lambda$1 implements Consumer {
private LambdaPrinciple$$Lambda$1() {
}
@LambdaForm.Hidden
public void accept(Object object) {
LambdaPrinciple.lambda$main$0((String)object);
}
}
public static void main(String[] args) {
List<String> list = Arrays.asList(\"I\", \"Love\", \"You\");
list.forEach((Consumer<String>) LambdaMetafactory.metafactory(
null, null, null,
(Ljava / lang / Object;)V,
lambda$main$0(java.lang.String),
(Ljava / lang / String;)V)());
}
private static /* synthetic */ void lambda$main$0(String s) {
System.out.println(s);
}
}
至此,便可以清晰的看到Lambda表达式的编译和运行过程,也更好的理解了Lambda表达式它的本质是函数式接口的匿名子类的匿名对象!
本文的所有代码
可参考:https://github.com/aldalee/java-new-features/tree/master/lambda,稍有改动。
来源:https://www.cnblogs.com/aldalee/p/16772505.html
本站部分图文来源于网络,如有侵权请联系删除。