Skip to content

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);

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 表达式来表示它。

能够使用Lambda表达式的一个重要依据是必须有相应的函数式接口,所谓的函数式接口,指的就是“一个接口中有且只能有一个抽象方法”。也就是说,如果一个接口只有一个抽象方法,那么该接口就是一个函数式接口。
如果我们在接口上声明了 @FunctionalInterface 注解,那么编译器就会按照函数式接口的定义来要求该接口,也就是该接口中有且只能定义一个抽象方法,如果该接口中定义了多个或0个抽象方法,则程序编译时就会报错。

@FunctionalInterface
public interface Flyable {
// 在函数式接口中,我们有且只能定义一个抽象方法
void showFly();
// 但是,可以定义任意多个默认方法或静态方法
default void show() {
System.out.println("JDK1.8之后,接口还可以定义默认方法和静态方法");
}
}
  • 所需类型不同
    • 匿名内部类:可以是接口,抽象类,具体类。
    • Lambda表达式:只能是接口。
  • 使用限制不同
    • 如果接口中有且仅有一个抽象方法,可以使用Lambda表达式,也可以使用匿名内部类。
    • 如果接口中有多个抽象方法,则就只能使用匿名内部类,而不能使用Lambda表达式。
  • 实现原理不同
    • 匿名内部类:编译之后,会生成一个单独的.class字节码文件。
    • Lambda表达式:编译之后,没有生成一个单独的.class字节码文件。

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);
  • 无返回值&无参数
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("你好", "世界");
}
}
  • 有返回值&无参数
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));
}
}

在以上代码中,虽然Lambda表达式的语法已经很简洁了,但是Lambda表达式的语法格式还可以更加的精简,从而写出更加优雅的代码,但是相应的代码可读性也会变差。在以下的应用场景中,我们就可以对Lambda表达式的语法进行精简,场景如下:

  1. 形参类型 可以省略,如果需要省略,则每个形参的类型都要省略。
  2. 如果形参列表中只存在一个形参,那么形参类型和小括号都可以省略。
  3. 如果方法体 当中只有一行语句,那么方法体的大括号也可以省略。
  4. 如果方法体中只有一条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));
}
}
名字接口名对应的抽象方法
消费Consumervoid accept(T t);
生产SupplierT get();
转换Function<T, R>R apply(T t);
判断Predicateboolean test(T t);

所有上述的函数式接口均位于java.util.function包内,通常在函数接口可用的地方,都可以采用Lambda表达式。