视频教程笔记,视频地址见 深入理解 Java8+jdk8 源码级思想
Lambda 表达式
Lambda 表达式简介
介绍
Lambda 表达式可以认为是一种匿名函数(对 JAVA 而言,他是一个对象,此处暂且认为是一种匿名函数吧),简单地说,它是没有声明的方法,也即没有访问修饰符、返回值声明和名字。
作用
- 在 JAVA8 之前,无法将函数作为参数传递给一个方法,也无法声明返回一个函数的方法。Lambda 表达式为 JAVA 添加了缺失的函数式编程的特性,使我们能把函数作为一等公民看待
- 在将函数作为一等公民的语言中,Lambda 表达式的类型是函数。但是在 JAVA 中 Lambda 表达式是对象,他们必须依附于一类特别的对象类型——函数式接口。
Lambda 表达式的语法结构
- 一个 Lambda 表达式可以有零个或多个参数
- 参数的类型既可以明确声明,也可以根据上下文来推断。例如:
(int a)
与(a)
效果相同 - 所有参数需包含在圆括号内,参数之间用逗号相隔。例如:
(a, b)
或(int a, int b)
或(String a, int b, float c)
- 空圆括号代表参数集为空。例如:
() -> 42
- 当只有一个参数,且其类型可推导时,圆括号
()
可省略。例如:a -> return a*a
- Lambda 表达式的主体可包含零条或多条语句
- 如果 Lambda 表达式的主体只有一条语句,花括号
{}
可省略。匿名函数的返回类型与该主体表达式一致 - 如果 Lambda 表达式的主体包含一条以上语句,则表达式必须包含在花括号 `{} 中(形成代码块)。匿名函数的返回类型与代码块的返回类型一致,若没有返回则为空
函数式接口
函数式接口简介
定义
某个接口中有且只有一个抽象方法,此时该接口称为函数式接口。
如果接口中某个方法重写了 java.lang.Object 中的方法,则改方法不算接口的抽象方法。即下面代码声明的接口也是函数式接口
1
2
3
4
5
6
7
public interface MyInterface {
void test();
String toString();
}
几个知识点
- 如果在接口上添加了 FunctionalInterface 注解,则编译器会以函数式接口的定义来要求该接口
- 如果一个接口只有一个抽象方法,但是没有加上 FunctionalInterface注解,编译器也会认为该接口是函数式接口
- 函数式接口可以通过 lambda表达式、函数引用和构造函数引用的方式来创建
java8中常用的函数式接口
Function 接口详解
源码解析
1 | /** |
Function
函数接口一共有四个方法,其中有一个抽象方法,两个有 default 实现的方法,一个静态方法。
R apply(T t)
接收一个T
类型的参数,并有一个R
类型的返回值<V> java.util.function.Function<V, R> compose(java.util.function.Function<? super V, ? extends T> before)
和<V> java.util.function.Function<T, V> andThen(java.util.function.Function<? super R, ? extends V> after)
提供了两种组合处理行为。前者是在调用自己的apply
方法之前,先调用另外一个Function
接口的apply
方法;后者是先执行自己的apply
方法,再执行另外一个Function
接口的apply
方法。值得注意的是,这两个函数返回的是一个实现了apply
方法的新的Function
对象,而不是直接返回计算后的结果,所以在调用了这两个方法后,还需要.apply(T)
才能得到结果。<T> java.util.function.Function<T, T> identity()
用来直接返回输入的参数。
一个例子
1 | package info.andrewei; |
BIFunction 接口详解
源码解析
1 | /** |
可以类比 Function
来看。注意方法 <V> BiFunction<T, U, V> andThen(Function<? super R, ? extends V> after)
的参数为 Function
类型。原因也比较容易理解,因为 andThen
方法会先执行自己的 apply
方法,再执行传入的 Function
接口的 apply
方法。执行自己的 apply
方法只会有一个 R
类型的返回值,所以后面的 apply
方法只能有一个入参。
一个例子
1 | package info.andrewei; |
Predicate 接口详解
源码解析
1 |
|
该接口主要用于做判断,即是否满足条件
这种场景,一共有5个方法
boolean test(T t)
该方法接受一个T
类型的入参,并返回boolean
值Predicate<T> and(Predicate<? super T> other)
该方法允许传入另外一个Predicate
接口,只有两个Predicate
都判断为true
时,才会返回true
,即与
条件Predicate<T> or(Predicate<? super T> other)
对比上面的方法,上面的是与
条件,这个函数是或
条件Predicate<T> negate()
返回!test(t)
<T> Predicate<T> isEqual(Object targetRef)
判断两个object
是否相等。一眼看上去会感觉比较奇怪,这个函数实际上是通过出入的参数targetRef
生成一个<T> Predicate<T>
对象,即固定了相比较的两个object
中的一个targetRef
,后面再调用.test(obj)
来判断是否相等。
一个例子
1 | package info.andrewei; |
Supplier 接口详解
源码解析
1 | /** |
这个接口很简单,只有一个抽象方法,T get()
获取一个对象,每次获取的对象可以是相同的,也可以是不同的。