持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第31天,点击查看活动详情
Lambda 表达式介绍
Lambda是一个匿名函数,即没有函数名的函数
Lambda 表达式将函数式编程引入了 Java 语言中,它可以在多种场景下简化程序,并且可以突破传统形式的参数传递:在以前,方法的参数只能是一个变量或普通表达式,但在 JDK 8 以后,Lambda 表达式也可以作为方法的参数来传递,实际上 Lambda 表达式代表着一个匿名方法,因此换句话说,现在可以让一个方法作为另一个方法的参数存在。
刚刚提到Lambda 表达式代表着一个匿名方法,而匿名方法是由参数列表和方法体两部分组成的,Lambda 表达式也必须包含这两部分。即Lambda表达式简化的是方法
Java 中的 Lambda 表达式是用箭头符号(–>)将参数列表和方法体连接起来的,因此一个 Lambda 表达式由以下三部分组成。
- 用逗号分隔的参数列表;
- 箭头符号(–>);
- 方法体(表达式或代码块)。
Lambda 表达式使用
之前我们创建并启动线程是这样的。
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("Hello World");
}
}) .start();
这里new Thread()是一个构造方法,其参数是 Runnable 接口类型的,并且根据多线程的知识可知在 Runnable 接口中只有 run()一个方法。通过观察发现,run()方法的参数列表为空,并且方法体就是{System.out.println("Hello World");},因此 run()方法对应的 Lambda 表达式形式就是()->{System.out.println("Hello World");}。
用Lambda就是这样
/**
* Lambda 表达式简化代码
*/
public class TestLambda {
public static void main(String[] args) {
new Thread(
//Lambda表达式
() -> { System.out.println("Hello World"); }
) .start();
}
}
函数式接口和Lambda 表达式配合使用
函数式接口介绍
在TestLambda 中的代码,在用 Lambda 表达式简化 new Thread(Runnable r) 的参数时,这个 Runnable 类型参数有什么特点呢? 🤔
在 Runnable 接口中,有且仅有一个抽象方法!也就是说,在使用 Lambda 代替匿名内部类时,该内部类(或接口)中只能有一个抽象方法。
从 JDK 8 开始,如果一个接口中有且仅有一个抽象方法,那么这个接口就称为函数式接口,并且可以选择性的使用 @FunctionalInterface 注解进行标注。@FunctionalInterface 是 JDK 8 引入的内建注解。
在 JDK 8 以后定义 Runnable 的源码如下所示:
@FunctionalInterface
public interface Runnable {
public abstract void run();
}
为了方便使用 Lambda 表达式,JDK8 提供了 java.util.function 包,该包下面的接口全部都是函数式接口。
其中最常用的是 Consumer、Supplier、Function 和 Predicate 四个接口,通常它们称为四大函数式接口。
| 函数式接口 | 接口中定义的唯一抽象方法 | 含义 |
|---|---|---|
| Consumer | void accept(T t) | 有输入参数,无返回值,称为消费型接口。 |
| Supplier | T get() | 无输入参数,有返回值,称为供给型接口。 |
| Function<T, R> | R apply(T t) | 有输入参数,有返回值,称为函数型接口。 |
| Predicate | boolean test(T t); | 对输入进行断言,并将断言的结果以布尔形式返回,称为断言型接口。 |
Lambda 实现自定义函数式接口
第一步:定义一个函数接口
// 自定义的函数式接口
@FunctionalInterface interface MyLambda {
void method();
}
第二步:使用自定义函数接口
public class TestLambda2 {
public TestLambda2(MyLambda lambda) {
lambda.method();
}
public static void main(String[] args) {
new TestLambda2(
() -> { System.out.println("Hello Lambda");
});
}
Lambda 表达式除了作为方法的参数以外,还能够作为赋值运算符的右值。
如供给型函数式接口 Supplier 中定义了一个没有输入参数、有返回值的 get() 函数,那么凡是符合以下形式的表达式:
() -> {return ...}
Lambda 表达式使用函数型接口
Function 接口来进行操作:
// Lambda表达式
Function<String, Integer> func = (String arg) -> {
return Integer.parseInt(arg);
};
// 省略了输入参数的类型
Function<String, Integer> func1 = (arg) -> {
return Integer.parseInt(arg);
};
// 省略了输入参数类型、return关键字和{}
Function<String, Integer> func2 = (arg) -> Integer.parseInt(arg);
在使用 Lambda 表达式的形式给变量赋值之后,如何使用呢?
赋值符号的左侧 Function 接口中定义了一个抽象方法 Integer apply(String t),赋值符号的右侧是用 Lambda 表达性实现的一个匿名方法,二者就是一对一的对应关系。
换句话说,程序中 Lambda 表达式就是对 Function 接口中 apply() 的具体实现。
也就是在调用赋值符号左侧对象的 apply() 方法时,就会执行赋值符号右侧 Lambda 表达式。
使用 Lambda 表达式对集合进行遍历
public static void main(String[] args) {
ArrayList<String> names = new ArrayList();
names.add("张三");
names.add("李四");
names.add("王五");
names.add("赵六");
names.forEach(name -> System.out.println(name)); }
总结
- Lambda表达式前提是在 函数式接口里面(函数式接口:一个接口里面有且仅有一个抽象方法),使用Lambda表达式的目标是简化这个唯一的抽象方法
- 多态:模拟汉语是“歧义”。 比如打:打什么?打球还是打水是不一样的。 但是Lambda表达式是没有歧义的,因为他用于简化的方法是唯一的,特定的
- 怎么知道Lambda简化是哪个方法呢?比如
在这里我们可以发现,Lambda出现位置是Thread构造方法里面,然后查询Thread构造方法,发现他构造方法是一个Runable接口,然后查询Runable发现他里面只有一个run方法,所以这里的Lambda简化的是run方法