深入理解Lambda表达式(lambda表达式底层原理)

  本篇文章为你整理了深入理解Lambda表达式(lambda表达式底层原理)的详细内容,包含有lambda表达式的作用与好处 lambda表达式底层原理 lambda表达式的用法 lambda表达式本质是什么 深入理解Lambda表达式,希望能帮助你了解 深入理解Lambda表达式。

   本文首先介绍什么是Lambda表达式,然后讲解它的语法规则,接着对Lambda表达式的应用进行举例,最后探讨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

  

 

  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 -

  

 

  注意:

  这里的抽象方法是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 weve 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 LambdaPrincipl。

郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。

留言与评论(共有 条评论)
   
验证码: