System.out.println(parameter1.test(10));
System.out.println(parameter2.test(10));
System.out.println(parameter3.test(10));
}
private static class Impl implements SingleReturnSingleParameter {
@Override
public int test(int a) {
return a * a;
}
}
}
### **22.1.3. Lambda表达式对接口的要求**
虽然说, lambda表达式可以在一定程度上简化接口的实现。 但是, 并不是所有的接口都可以使用lambda表达式来简洁实现的。
lambda表达式毕竟只是一个匿名方法。 当实现的接口中的方法过多或者多少的时候, lambda表达式都是不适用的。
lambda表达式,只能实现**函数式接口**。
**22.1.4. 函数式接口**
**22.1.4.1. 基础概念**
如果说, 一个接口中, 要求实现类**必须**实现的抽象方法, 有且只有一个! 这样的接口, 就是函数式接口。
// 这个接口中, 有且只有一个方法, 是实现类必须实现的, 因此是一个函数式接口
interface Test1 {
void test();
}
// 这个接口中, 实现类必须要实现的方法, 有两个! 因此不是一个函数式接口
interface Test2 {
void test1();
void test2();
}
// 这个接口中, 实现类必须要实现的方法, 有零个! 因此不是一个函数式接口
interface Test3 {
}
// 这个接口中, 虽然没有定义任何的方法, 但是可以从父接口中继承到一个抽象方法的。 是一个函数式接口
interface Test4 extends Test1 {
}
// 这个接口, 虽然里面定义了两个方法, 但是defualt方法子类不是必须实现的。
// 因此, 实现类实现这个接口的时候, 必须实现的方法只有一个! 是一个函数式接口。
interface Test5 {
void test5();
default void test() {}
}
// 这个接口中的 toString 方法, 是Object类中定义的方法。
// 此时, 实现类在实现接口的时候, toString可以不重写的! 因为可以从父类Object中继承到!
// 此时, 实现类在实现接口的时候, 有且只有一个方法是必须要重写的。 是一个函数式接口!
interface Test6 {
void test6();
String toString();
}
思考题: 下面的两个接口是不是函数式接口?
interface Test7 {
String toString();
}
interface Test8 {
void test();
default void test1() {}
static void test2() {}
String toString();
}
#### **22.1.4.2. @FunctionalInterface**
是一个注解, 用在接口之前, 判断这个接口是否是一个函数式接口。 如果是函数式接口, 没有任何问题。 如果不是函数式接口, 则会报错。 功能类似于 @Override。
@FunctionalInterface
interface FunctionalInterfaceTest {
void test();
}
**22.1.4.3. 系统内置的若干函数式接口**
![](https://imgconvert.csdnimg.cn/aHR0cHM6Ly9waWMzLnpoaW1nLmNvbS84MC92Mi1kMzU2YWE4OWMzZTg4Mjk2YWMzODViZDQyNDNjNzk0YV83MjB3LmpwZw?x-oss-process=image/format,png)
**22.2. Lambda表达式的语法**
### **22.2.1. Lambda表达式的基础语法**
lambda表达式, 其实本质来讲, 就是一个匿名函数。 因此在写lambda表达式的时候, 不需要关心方法名是什么。
实际上, 我们在写lambda表达式的时候, 也不需要关心返回值类型。
我们在写lambda表达式的时候, 只需要关注两部分内容即可: **参数列表** 和 **方法体**
**lambda表达式的基础语法:**
(参数) ->{
方法体
};
**参数部分** : 方法的参数列表, 要求和实现的接口中的方法参数部分一致, 包括参数的数量和类型。
**方法体部分 :** 方法的实现部分, 如果接口中定义的方法有返回值, 则在实现的时候, 注意返回值的返回。
**->** : 分隔参数部分和方法体部分。
/**
-
@Description
*/
public class Syntax {
public static void main(String[] args) {
// 1. 无参、无返回值的方法实现
NoneReturnNoneParameter lambda1 = () -> {
System.out.println(“无参、无返回值方法的实现”);
};
lambda1.test();// 2. 有参、无返回值的方法实现 NoneReturnSingleParameter lambda2 = (int a) -> { System.out.println("一个参数、无返回值方法的实现: 参数是 " + a); }; lambda2.test(10); // 3. 多个参数、无返回值方法的实现 NoneReturnMutipleParameter lambda3 = (int a, int b) -> { System.out.println("多个参数、无返回值方法的实现: 参数a是 " + a + ", 参数b是 " + b); }; lambda3.test(10, 20); // 4. 无参、有返回值的方法的实现 SingleReturnNoneParameter lambda4 = () -> { System.out.println("无参、有返回值方法的实现"); return 666; }; System.out.println(lambda4.test()); // 5. 一个参数、有返回值的方法实现 SingleReturnSingleParameter lambda5 = (int a) -> { System.out.println("一个参数、有返回值的方法实现: 参数是 " + a); return a * a; }; System.out.println(lambda5.test(9)); // 6. 多个参数、有返回值的方法实现 SingleReturnMutipleParameter lambda6 = (int a, int b) -> { System.out.println("多个参数、有返回值的方法实现: 参数a是 " + a + ", 参数b是 " + b); return a * b; }; System.out.println(lambda6.test(10, 20));
}
}
### **22.2.2. Lambda表达式的语法进阶**
在上述代码中, 的确可以使用lambda表达式实现接口, 但是依然不够简洁, 有简化的空间。
#### **22.2.2.1. 参数部分的精简**
* 参数的类型
+ 由于在接口的方法中,已经定义了每一个参数的类型是什么。 而且在使用lambda表达式实现接口的时候, 必须要保证参数的数量和类型需要和接口中的方法保持一致。 因此, 此时lambda表达式中的参数的类型可以省略不写。
+ 注意事项:
- 如果需要省略参数的类型, 要保证: 要省略, 每一个参数的类型都必须省略不写。 绝对不能出现, 有的参数类型省略了, 有的参数类型没有省略。
// 多个参数、无返回值的方法实现
NoneReturnMutipleParameter lambda1 = (a, b) -> {
System.out.println("多个参数、无返回值方法的实现: 参数a是 " + a + ", 参数b是 " + b);
};
* 参数的小括号
+ 如果方法的参数列表中的参数数量 **有且只有一个** ,此时,参数列表的小括号是可以省略不写的。
+ 注意事项:
- 只有当参数的数量是一个的时候, 多了、少了都不能省略。
- 省略掉小括号的同时, 必须要省略参数的类型。
// 有参、无返回值的方法实现
NoneReturnSingleParameter lambda2 = a -> {
System.out.println("一个参数、无返回值方法的实现: 参数是 " + a);
};
**22.2.2.2. 方法体部分的精简**
* 方法体大括号的精简
+ 当一个方法体中的逻辑, 有且只有一句的情况下, 大括号可以省略。
// 有参、无返回值的方法实现
NoneReturnSingleParameter lambda2 = a -> System.out.println("一个参数、无返回值方法的实现: 参数是 " + a);
* return的精简
+ 如果一个方法中唯一的一条语句是一个返回语句, 此时在省略掉大括号的同时, 也必须省略掉return。
SingleReturnMutipleParameter lambda3 = (a, b) -> a + b;
**22.3. 函数引用**
lambda表达式是为了简化接口的实现的。 在lambda表达式中, 不应该出现比较复杂的逻辑。 如果在lambda表达式中出现了过于复杂的逻辑, 会对程序的可读性造成非常大的影响。 如果在lambda表达式中需要处理的逻辑比较复杂, 一般情况会单独的写一个方法。 在lambda表达式中直接引用这个方法即可。
>
> 或者, 在有些情况下, 我们需要在lambda表达式中实现的逻辑, 在另外一个地方已经写好了。 此时我们就不需要再单独写一遍, 只需要直接引用这个已经存在的方法即可。
>
**函数引用:** 引用一个已经存在的方法, 使其替代lambda表达式完成接口的实现。
**22.3.1. 静态方法的引用**
* **语法:**
+ 类::静态方法
* **注意事项:**
+ 在引用的方法后面, 不要添加小括号。
+ 引用的这个方法, 参数(数量、类型) 和 返回值, 必须要跟接口中定义的一致。
* **示例:**
/**
-
@Description
*/
public class Syntax1 {
// 静态方法的引用
public static void main(String[] args) {
// 实现一个多个参数的、一个返回值的接口
// 对一个静态方法的引用
// 类::静态方法
SingleReturnMutipleParameter lambda1 = Calculator::calculate;
System.out.println(lambda1.test(10, 20));
}private static class Calculator {
public static int calculate(int a, int b) {
// 稍微复杂的逻辑:计算a和b的差值的绝对值
if (a > b) {
return a - b;
}
return b - a;
}
}
}
**22.3.2. 非静态方法的引用**
* **语法:**
+ 对象::非静态方法
* **注意事项:**
+ 在引用的方法后面, 不要添加小括号。
+ 引用的这个方法, 参数(数量、类型) 和 返回值, 必须要跟接口中定义的一致。
* **示例:**
/**
-
@Description
*/
public class Syntax2 {
public static void main(String[] args) {
// 对非静态方法的引用,需要使用对象来完成
SingleReturnMutipleParameter lambda = new Calculator()::calculate;
System.out.println(lambda.test(10, 30));
}private static class Calculator {
public int calculate(int a, int b) {
return a > b ? a - b : b - a;
}
}
}
### **22.3.3. 构造方法的引用**
![img](https://img-blog.csdnimg.cn/img_convert/b9da37f24d0c7bf47eab11474ccb1c91.png)
![img](https://img-blog.csdnimg.cn/img_convert/ab6641bd585a9694d6068299a766ed76.png)
**网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。**
**[需要这份系统化资料的朋友,可以戳这里获取](https://bbs.csdn.net/topics/618545628)**
**一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!**
``
### **22.3.3. 构造方法的引用**
[外链图片转存中...(img-TMtQ9ucQ-1714752476161)]
[外链图片转存中...(img-yHczpBxE-1714752476162)]
**网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。**
**[需要这份系统化资料的朋友,可以戳这里获取](https://bbs.csdn.net/topics/618545628)**
**一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!**