源码元宇宙-lambda表达式底层执行解析

ywz888
发布于 2022-10-9 10:33
浏览
0收藏

背景

 

3月28日那天,咱们 用户群 里,朋友让我写篇lambda表达式的底层执行解析。拖了快20天了,今天就来聊聊这个问题。

 

《深入理解函数式编程》里讲到lambda表达式本质是一个匿名的内联函数。不从Java角度,Lambda本身是计算机编程语言,Lambda 表达式是一个匿名函数,它可以包含表达式和语句,并且可用于创建委托或表达式目录树类型。那篇文章讲解了匿名内部类与函数式编程怎么转换,怎么debug调试。

 

对面试而言,这些内容足够了。朋友提出想了解lambda表达式的底层执行解析。说明是真的对技术有追求,是把问题思考细化了,这个是很赞的。

 

《面试官问我:你做事仔细吗?》一文中,我提出了仔细意识的重要性。大家想想,咱们上高三的时候,一般的学生是不是所有课本上讲的数学题都会做?那为什么考满分的却寥寥无几。老师告诉我们这不是马虎不马虎的事情,就是能力的差距。

 

能力的差距就在于把问题考虑仔细周全的能力。互联网的上半场,IT能力刚起步,就好像教育水平低的学校,能把课学明白的就不多。而互联网的下半场好比是重点高中,一个差生有可能是小学的时候的班长。学习能力都没有问题,想达到更高的深度和广度靠的就是把问题细化分解这种“额外”能力的差距。

 

总结来说:越是基础的知识,越需要沉下心,将问题细化。

 

函数式接口

 

 

Lambda是函数式编程思想的一个重要体现,让我们不用关注是什么对象,而是更关注我们对数据进行了什么操作。在函数式编程思想中,函数是"第一等公民"。所谓"第一等公民"(first class),指的是函数与其他数据类型一样,处于平等地位,可以赋值给其他变量,也可以作为参数,传入另一个函数,或者作为别的函数的返回值。

 

Lambda表达式的入参都是函数,所以咱们先来看看函数在Java中的定义。

源码元宇宙-lambda表达式底层执行解析-鸿蒙开发者社区

匿名内部类,类是使用时实现的,在Lambda表达式中,入参都是函数接口来定义的,函数接口大多都带有 @FunctionalInterface 注解。先看看这个注解的定义。

/**
 * An informative annotation type used to indicate that an interface
 * type declaration is intended to be a <i>functional interface</i> as
 * defined by the Java Language Specification.
 *
 * Conceptually, a functional interface has exactly one abstract
 * method.  Since {@linkplain java.lang.reflect.Method#isDefault()
 * default methods} have an implementation, they are not abstract.  If
 * an interface declares an abstract method overriding one of the
 * public methods of {@code java.lang.Object}, that also does
 * <em>not</em> count toward the interface's abstract method count
 * since any implementation of the interface will have an
 * implementation from {@code java.lang.Object} or elsewhere.
 *
 * <p>Note that instances of functional interfaces can be created with
 * lambda expressions, method references, or constructor references.
 *
 * <p>If a type is annotated with this annotation type, compilers are
 * required to generate an error message unless:
 *
 * <ul> * <li> The type is an interface type and not an annotation type, enum, or class.
 * <li> The annotated type satisfies the requirements of a functional interface.
 * </ul> *
 * <p>However, the compiler will treat any interface meeting the
 * definition of a functional interface as a functional interface
 * regardless of whether or not a {@code FunctionalInterface}
 * annotation is present on the interface declaration.
 *
 * @jls 4.3.2. The Class Object
 * @jls 9.8 Functional Interfaces
 * @jls 9.4.3 Interface Method Body
 * @since 1.8
 */
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface FunctionalInterface {}

 

这个接口的注释说明了函数接口的要点:

 

1)函数接口有且只有一个抽象方法

源码元宇宙-lambda表达式底层执行解析-鸿蒙开发者社区

2)可以有其他default 实现的方法,这不违背有且只有一个抽象方法的原则,因为实现了就不是抽象的

源码元宇宙-lambda表达式底层执行解析-鸿蒙开发者社区

3)可以有额外的 java.lang.Object 的 public 的抽象类,这不违背有且只有一个抽象方法的原则,因为 Object 或者其他的地方 已经实现了这个方法

源码元宇宙-lambda表达式底层执行解析-鸿蒙开发者社区

4)函数式接口可以通过lambda表达式、方法引用或者构造方法引用来创建实例

源码元宇宙-lambda表达式底层执行解析-鸿蒙开发者社区

5) 如果加上了 @FunctionalInterface 注解,编译器就会校验是否符合下面规范,不符合就会报编译错误:

规范规定此注解只能加在接口上,不能加在注解类型、枚举类型和类上,并且这个接口还要满足上面那4个条件。

 

 

6)满足上面5个条件,即使不加 @FunctionalInterface 注解,也是函数式接口

 

 

咱们来动手实践一下,写个小例子运行一下:

源码元宇宙-lambda表达式底层执行解析-鸿蒙开发者社区

我们定义了一个接口的lambda表达式形式的实现,发现这个实现是Java自己的实现类,类名是Java自己生成的匿名内部类,其父类是Object,实现了YunaInterface这一个接口。

 

 

本系列的所有代码文字在 https://github.com/xiexiaojing/yuna 里可以找到。

 

forEach方法引用lambda表达式

 

先看一个简单的例子,一个list进行forEach:

源码元宇宙-lambda表达式底层执行解析-鸿蒙开发者社区

跟进源码里看一看:、

源码元宇宙-lambda表达式底层执行解析-鸿蒙开发者社区

这里就通过lambda表达式把实现了Consumer接口的行为动作传给了forEach方法。

 

咱们来看一看Consumer接口:

源码元宇宙-lambda表达式底层执行解析-鸿蒙开发者社区

首先还是来翻译一下接口说明:

 

这个函数代表了一个操作,这个操作只接受一个参数并且不返回结果。但是和大多数函数式接口不同,它会产生副作用。

 

 

这段说明很好的诠释了类名Consumer和accept方法:只accept一个参数T,并且消费掉了不会返回结果。forEach方法就是对下面匿名内部类的简写:

源码元宇宙-lambda表达式底层执行解析-鸿蒙开发者社区

Alter+Enter快捷键可以对其进行化简,最简的写法是下面方法引用的形式:

源码元宇宙-lambda表达式底层执行解析-鸿蒙开发者社区

大家可能注意到Consumer里有个带有默认实现的andThen方法,这个方法有需要的时候这样可以使用:

源码元宇宙-lambda表达式底层执行解析-鸿蒙开发者社区

总结

 

 

本文说明了函数式接口特性和作用,并在此基础上介绍了forEach这个方法的底层实现。相信大家看了文章应该对底层执行怎么去深入了解有了思路和方法,可以自己去研究其他的函数了。

 

文章转载自公众号:编程一生

分类
已于2022-10-9 10:34:20修改
收藏
回复
举报
回复
    相关推荐