Lambda表达式
Lambda表达式作为JDK 1.8的新特性,能够替换大多数匿名内部类,使得Java代码更加优雅,特别是在集合遍历和其他集合操作时,它能显著优化代码结构。
- 例如,要对List集合进行“降序”排序,通常需要使用匿名内部类,这会使代码变得复杂和冗长,如下所示:
// 方式一:使用匿名内部类来实现List<Integer> list = Arrays.asList(3, 6, 1, 7, 2, 5, 4);Collections.sort(list, new Comparator<Integer>() { @Override public int compare(Integer o1, Integer o2) { return o2 - o1; }});System.out.println("排序后:" + list);- 除了通过匿名内部类实现List集合的“降序”排序外,还可以利用Lambda表达式来完成,这种方式代码更加优雅简洁,具体代码如下:
// 方式二:使用Lambda表达式来实现List<Integer> list = Arrays.asList(3, 6, 1, 7, 2, 5, 4);Collections.sort(list, (o1, o2) -> o2 - o1);System.out.println("排序后:" + list);1. 函数式编程思想的概述
Section titled “1. 函数式编程思想的概述”Java从诞生之日起就一直倡导“一切皆对象”,在Java语言中面向对象(OOP)编程就是一切,但是随着Python和Scala等语言的崛起和新技术的挑战,Java也不得不做出调整以便支持更加广泛的技术要求,即Java语言不但支持OOP还支持OOF(面向函数编程)。
JDK1.8引入Lambda表达式之后,Java语言也开始支持函数式编程,但是Lambda表达式不是Java语言最早使用的,目前C++、C#、Python、Scala等语言都支持Lambda表示。
- 面向对象的思想
- 做一件事情,找一个能解决这个事情的对象,然后调用对象的方法,最终完成事情。
- 函数式编程思想
- 只要能获得结果,谁去做的,怎么做的都不重要,重视的是结果,不重视实现过程。
在函数式编程语言中,函数被视为一等公民。虽然 Lambda 表达式在逻辑上是函数,但在 Java 中,它们实际上是一个对象,必须依附于一个特定类型的对象,即函数式接口。简而言之,JDK 1.8 中的 Lambda 表达式就是函数式接口的实例。因此,只要一个对象是函数式接口的实例,就可以用 Lambda 表达式来表示它。
2. 如何去理解函数式接口
Section titled “2. 如何去理解函数式接口”能够使用Lambda表达式的一个重要依据是必须有相应的函数式接口,所谓的函数式接口,指的就是“一个接口中有且只能有一个抽象方法”。也就是说,如果一个接口只有一个抽象方法,那么该接口就是一个函数式接口。
如果我们在接口上声明了 @FunctionalInterface 注解,那么编译器就会按照函数式接口的定义来要求该接口,也就是该接口中有且只能定义一个抽象方法,如果该接口中定义了多个或0个抽象方法,则程序编译时就会报错。
@FunctionalInterfacepublic interface Flyable { // 在函数式接口中,我们有且只能定义一个抽象方法 void showFly(); // 但是,可以定义任意多个默认方法或静态方法 default void show() { System.out.println("JDK1.8之后,接口还可以定义默认方法和静态方法"); }}3. Lambda和匿名内部类
Section titled “3. Lambda和匿名内部类”- 所需类型不同
- 匿名内部类:可以是接口,抽象类,具体类。
- Lambda表达式:只能是接口。
- 使用限制不同
- 如果接口中有且仅有一个抽象方法,可以使用Lambda表达式,也可以使用匿名内部类。
- 如果接口中有多个抽象方法,则就只能使用匿名内部类,而不能使用Lambda表达式。
- 实现原理不同
- 匿名内部类:编译之后,会生成一个单独的.class字节码文件。
- Lambda表达式:编译之后,没有生成一个单独的.class字节码文件。
4. Lambda表达式的语法
Section titled “4. Lambda表达式的语法”Lambda表达式的本质是一个匿名函数,它简化了函数的语法,只需关注参数列表和函数体。在Java中,Lambda表达式的格式是“(参数列表) -> {函数体}”,其中“->”是Lambda操作符,参数列表对应接口中方法的参数,函数体则是该方法的实现。
5. 匿名内部类演化为Lambda表达式
Section titled “5. 匿名内部类演化为Lambda表达式”- 匿名类
List<Integer> list = Arrays.asList(3, 6, 1, 7, 2, 5, 4);Collections.sort(list, new Comparator<Integer>() { @Override public int compare(Integer o1, Integer o2) { return o2 - o1; }});System.out.println("排序后:" + list);- Lambda表达式
List<Integer> list = Arrays.asList(3, 6, 1, 7, 2, 5, 4);Collections.sort(list, (Integer o1, Integer o2) -> { return o2 - o1;});System.out.println("排序后:" + list);6. Lambda表达式的基本使用
Section titled “6. Lambda表达式的基本使用”6.1. 无返回值函数式接口
Section titled “6.1. 无返回值函数式接口”- 无返回值&无参数
interface NoParameterNoReturn { void test();}
public class Test01 { public static void main(String[] args) { // 方式一:使用匿名内部类来实现 NoParameterNoReturn obj1 = new NoParameterNoReturn() { @Override public void test() { System.out.println("无参无返回值"); } }; obj1.test();
// 方式二:使用Lambda表达式来实现 NoParameterNoReturn obj2 = () -> { System.out.println("无参无返回值"); }; obj2.test(); }}- 无返回值&一个参数
interface OneParameterNoReturn { void test(int num);}
public class Test01 { public static void main(String[] args) { // 方式一:使用匿名内部类来实现 OneParameterNoReturn obj1 = new OneParameterNoReturn() { @Override public void test(int num) { System.out.println("无返回值一个参数 --> " + num); } }; obj1.test(10);
// 方式二:使用Lambda表达式来实现 OneParameterNoReturn obj2 = (int num) -> { System.out.println("无返回值一个参数 --> " + num); }; obj2.test(20); }}- 无返回值&多个参数
interface MoreParameterNoReturn { void test(String str1, String str2);}public class Test01 { public static void main(String[] args) { // 方式一:使用匿名内部类来实现 MoreParameterNoReturn obj1 = new MoreParameterNoReturn() { @Override public void test(String str1, String str2) { System.out.println(str1 + " : " + str2); } }; obj1.test("hello", "world");
// 方式二:使用Lambda表达式来实现 MoreParameterNoReturn obj2 = (String str1, String str2) -> { System.out.println(str1 + " : " + str2); }; obj2.test("你好", "世界"); }}6.2. 有返回值函数接口
Section titled “6.2. 有返回值函数接口”- 有返回值&无参数
interface NoParameterHasReturn { int test();}
public class Test01 { public static void main(String[] args) { // 方式一:使用匿名内部类来实现 NoParameterHasReturn obj1 = new NoParameterHasReturn() { @Override public int test() { return 520; } }; System.out.println(obj1.test()); // 输出:520
// 方式二:使用Lambda表达式来实现 NoParameterHasReturn obj2 = () -> { return 1314; }; System.out.println(obj2.test()); // 输出:1314 }}- 有返回值&一个参数
// 情况二:有返回值一个参数interface OneParameterHasReturn { String test(double num);}
public class Test01 { public static void main(String[] args) { // 方式一:使用匿名内部类来实现 OneParameterHasReturn obj1 = new OneParameterHasReturn() { @Override public String test(double num) { return "传入的小数为:" + num; } }; System.out.println(obj1.test(520.0));
// 方式二:使用Lambda表达式来实现 OneParameterHasReturn obj2 = (double num) -> { return "传入的小数为:" + num; }; System.out.println(obj2.test(1314.0)); }}- 有返回值&多个参数
interface MoreParameterHasReturn { String test(int num1, int num2);}public class Test01 { public static void main(String[] args) { // 方式一:使用匿名内部类来实现 MoreParameterHasReturn obj1 = new MoreParameterHasReturn() { @Override public String test(int num1, int num2) { return "运算的结果为:" + (num1 + num2); } }; System.out.println(obj1.test(10, 20));
// 方式二:使用Lambda表达式来实现 MoreParameterHasReturn obj2 = (int num1, int num2) -> { return "运算的结果为:" + (num1 + num2); }; System.out.println(obj2.test(20, 30)); }}7. Lambda表达式的语法精简
Section titled “7. Lambda表达式的语法精简”在以上代码中,虽然Lambda表达式的语法已经很简洁了,但是Lambda表达式的语法格式还可以更加的精简,从而写出更加优雅的代码,但是相应的代码可读性也会变差。在以下的应用场景中,我们就可以对Lambda表达式的语法进行精简,场景如下:
- 形参类型 可以省略,如果需要省略,则每个形参的类型都要省略。
- 如果形参列表中只存在一个形参,那么形参类型和小括号都可以省略。
- 如果方法体 当中只有一行语句,那么方法体的大括号也可以省略。
- 如果方法体中只有一条return语句,那么大括号可以省略,且必须去掉return关键字。
public class LambdaTest { public static void main(String[] args) { // (1)形参类型可以省略,如果需要省略,每个形参的类型都要省略。 // 没有精简的Lambda表达式代码 MoreParameterNoReturn obj1 = (String str1, String str2) -> { System.out.println(str1 + " : " + str2); }; obj1.test("hello", "world"); // 精简之后的Lambda表达式代码 MoreParameterNoReturn obj2 = (str1, str2) -> { System.out.println(str1 + " : " + str2); }; obj2.test("你好", "世界");
// (2)如果形参列表中只有一个形参,那么形参类型和小括号都可以省略。 // 没有精简的Lambda表达式代码 OneParameterHasReturn obj3 = (double num) -> { return "传入的小数为:" + num; }; System.out.println(obj3.test(520.0)); // 精简之后的Lambda表达式代码 OneParameterHasReturn obj4 = num -> { return "传入的小数为:" + num; }; System.out.println(obj4.test(1314.0));
// (3)如果方法体当中只有一行代码,那么方法体的大括号也可以省略。 // 没有精简的Lambda表达式代码 NoParameterNoReturn obj5 = () -> { System.out.println("无参无返回值"); }; obj5.test(); // 精简之后的Lambda表达式代码 NoParameterNoReturn obj6 = () -> System.out.println("无参无返回值"); obj6.test();
// (4)方法体中只有一条return语句,则大括号可以省略,且必须去掉return关键字 // 没有精简的Lambda表达式代码 MoreParameterHasReturn obj7 = (int a, int b) -> { return "运算的结果为:" + (a + b); }; System.out.println(obj7.test(10, 20)); // 精简之后的Lambda表达式代码 MoreParameterHasReturn obj8 = (a, b) -> "运算的结果为:" + (a + b); System.out.println(obj8.test(20, 30)); }}8. 四个基本的函数式接口
Section titled “8. 四个基本的函数式接口”| 名字 | 接口名 | 对应的抽象方法 |
|---|---|---|
| 消费 | Consumer | void accept(T t); |
| 生产 | Supplier | T get(); |
| 转换 | Function<T, R> | R apply(T t); |
| 判断 | Predicate | boolean test(T t); |
所有上述的函数式接口均位于java.util.function包内,通常在函数接口可用的地方,都可以采用Lambda表达式。