百木园-与人分享,
就是让自己快乐。

深入理解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

说明如下:

  1. parameters是函数的参数列表
    当参数不止一个时,()不可省略,否则可以省略()和类型;
    当函数式接口中抽象方法的参数列表的参数类型可以自动推断时,可以省略对应的类型
  2. statements是执行语句
    当函数体仅有一个语句,可以省略{},否则不可省略;
  3. expression是表达式
    当函数体只有一个表达式,且运算结果匹配返回值类型,可以省略return关键字
  4. ->是使用指定参数去完成某个功能,不可省略

常见的函数式接口

  • 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),它传入一个泛型参数并返回truefalse

案例

给定一个字符串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;
    }
}

注意:

  1. 这里的抽象方法是apply,可以从数学上理解Function接口,即\\(f:T \\rightarrow R\\),输入一个\\(T\\)可以返回一个\\(R\\)\\(f\\)就是定义的一套规则,交由apply执行。
  2. 对于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所示:
image.png

详细过程分析

通过反编译得到字节码文件,可以分析Lambda表达式的本质。将cfr-0.145.jar包添加到LambdaPrinciple类的编译路径,如图2所示。
Snipaste_2022-10-09_14-01-27.png
执行反编译命令:java -jar cfr-0.145.jar LambdaPrinciple.class --decodelambdas false,如图3所示。
2.png
得到反编译的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
本站部分图文来源于网络,如有侵权请联系删除。

未经允许不得转载:百木园 » 深入理解Lambda表达式

相关推荐

  • 暂无文章