本文为原创文章,转载请注明出处
查看[Java]系列内容请点击:https://www.jianshu.com/nb/45938443
类比数学概念
lambda实际上就是λ
,使用lambda的目的是为了减少内部类的个数,减少代码的复杂程度,并且一定程度上节省内存。
简单理解,lambda表达式用来定义一段代码,这段代码当需要的时候才会执行,不需要的时候不执行,我们在学习数学的时候,表达式一般是指一个式子,不用计算具体式子的值,如:
f = a + b 是一个表达式
lambda也是类似,类似定义一个计算机中的式子,在Java中,我们可以这么定义:
var f = (a, b) -> a + b
这样就定义好了一个表达式。
如何使用一个lambda表达式
要使用一个lambda表达式,我们需要以下三要素:
- 一个仅有一个方法需要复写的接口A(注意:仅有一个需要复写的方法)
- 一个以A为参数的方法B
- 一个调用B的程序C
其中,在C中定义lambda表达式,并将表达式作为参数传给B,在B中进行调用A接口对应的方法。这种方式也称为『函数式编程』。
一个示例:
// 接口A
public interface MyInterface {
String add(String a, String b);
}
public class MainTest {
// 调用程序C
public static void main(String[] args) {
MyInterface lambda = (a, b) -> a + b;
test("hello", " wanli", lambda);
}
// 方法B
public static void test(String a, String b, MyInterface lambda) {
System.out.println(lambda.add(a, b));
}
}
到这里你已经掌握了lambda表达式的基本过程,下面看lambda表达式定义的几种方式:
(参数类型1 参数名1, 参数类型2 参数名2...) -> {
函数体...
}
// 例如以上MainTest例子的lambda表达式的完全过程如下:
(String a, String b) -> {
return a + b;
}
表达式的简化写法:
一阶段简化
当从上下文中可以推断出参数类型的时候,参数类型可以省略。例如上面:
(a, b) -> {
return a + b;
}
注意1:当上下文不可推断出参数类型的时候,需要加上参数类型。
注意2:当不需要参数的时候,小括号不能省略
二阶段简化
当函数体代码只有一句时,可以省略大括号,例如上面表达式可以这么写:
(a, b) -> return a + b;
三阶段简化
如果函数体和返回值都是一句代码,return
也可以省略:
(a, b) -> a + b;
四阶段简化
若传入参数只有一个,小括号可以省略,上面表达式如果只有一个参数的话:
a -> "hello" + a;
五阶段简化:方法引用
如果接口A的方法所需要的参数与某方法X所需要的参数个数一致,A中的方法仅执行X方法,那么可以使用方法引用:双冒号,看如下示例:
public interface MyInterface {
void apply(String x);
}
public class MainTest {
public static void main(String[] args) {
test("hello", " wanli", System.out::println);
}
public static void test(String a, String b, MyInterface lambda) {
lambda.apply(a + b);
}
}
其中System.out::println
表示引用System
的out
对象中的println
方法。
至此,已经简化的不能更简化了。
方法引用
方法引用有三种情况:
object::instanceMethod
: 对象方法引用,表示object.instanceMethod(...)
Class::instanceMethod
: 隐式对象引用,表示(x, y) -> x.instanceMethod(y)
,第一个参数作为对象
Class::staticMethod
: 静态方法引用,表示Class.staticMethod(...)
请使用时注意区分。
对于新建对象,有构造方法引用,如:Person::new
表示new
一个Person
变量作用域
lambda表达式具有延迟执行的优点,就是说lambda表达式可以在定义的地方不执行,而是在其他线程中执行,所以在执行的过程中表达式之外的变量有没有变化是不一定的,甚至已经销毁。所以:
- 表达式内部的变量不能供外部访问
- 表达式只能访问外部的事实最终变量
什么是『事实最终变量』?当一个变量创建后没有修改其值,就是实时最终变量,一旦修改了值传入表达式内部,编译器会直接报错!
常用函数式接口
一般来说,是不需要自己定义函数式接口(上面的MyInterface
接口)的,在java.util.function
包中,Java内置了几个函数式接口:
函数式接口 | 抽象方法 | 描述 |
---|---|---|
Runnable | void run() | 作为无参运行 |
Supplier<T> | T get() | 获取一个T类型值 |
Consumer<T> | void accept(T t) | 消费一个T类型参数 |
Function<T,R> | R apply(T t) | 接收一个T类型参数,返回一个R类型参数 |
Predicate<T> | boolean test(T t) | 接收一个T类型参数,返回一个boolean类型参数 |
还有更多的函数式接口,请自行网上搜索。