我是陈皮,一个在互联网 Coding 的 ITer,个人微信公众号「陈皮的JavaLib」关注第一时间阅读最新文章。
Lambda 简介
Lambda 表达式是 Java 8 引入的一个重要新语法,是一种紧凑的传递代码的方式,即允许把代码作为一个方法的实参。从而可以写出更简洁,更灵活的代码,语言表达能力得到了提升。
案例分析
Java 编程中推荐面向接口编程,例如 test 方法需要一个 Human 接口对象。正常编程是编写一个 Human 接口的实现类,实例化一个实现类对象传递到方法中。
package com.chenpi;
/**
* @author 陈皮
* @version 1.0
* @description
* @date 2022/3/18
*/
public interface Human {
void speak();
}
package com.chenpi;
/**
* @author 陈皮
* @version 1.0
* @description
* @date 2022/3/18
*/
public class Man implements Human {
@Override
public void speak() {
System.out.println("I am man!");
}
}
package com.chenpi;
/**
* @author 陈皮
* @version 1.0
* @description
* @date 2022/3/18
*/
public class ChenPi {
public static void main(String[] args) {
test(new Man());
}
public static void test(Human human) {
human.speak();
}
}
如果我们不想编写一个 Human 的实现类,那么可以使用匿名内部类的形式,如下所示。
package com.chenpi;
/**
* @author 陈皮
* @version 1.0
* @description
* @date 2022/3/18
*/
public class ChenPi {
public static void main(String[] args) {
test(new Human() {
@Override
public void speak() {
System.out.println("I am man!");
}
});
}
public static void test(Human human) {
human.speak();
}
}
其实传递给 test 方法中的代码中,有用的代码就只有输出语句那一行,所以借助 Lambda 表达式,我们可以只传递有用的代码,不需要匿名内部类和实现类。
package com.chenpi;
/**
* @author 陈皮
* @version 1.0
* @description
* @date 2022/3/18
*/
public class ChenPi {
public static void main(String[] args) {
test(() -> System.out.println("I am man!"));
}
public static void test(Human human) {
human.speak();
}
}
Lambda 语法
语法:() -> {}
() :Lambda的形参列表,即接口中抽象方法的形参列表
-> :Lambda的操作符,参数列表和Lambda体的分隔符
{} :Lambda体,即实现了接口中的抽象方法的方法体
注:
1:参数列表的参数类型可以省略,Java可以根据上下文推断出来
2:如果参数列表只有一个,()可以省略
3:如果Lambda体只有一行语句,{}可以省略,并且语句末尾不能加;分号
4:如果Lambda体有返回值,而且只有一行语句,{}和return关键字都可以省略,并且语句末尾不能加;分号
Lambda 表达式其实是对某些接口的简单实现。但不是所有接口都可以使用 Lambda 表达式来实现,接口只能有一个抽象方法。但不要求接口只能有一个方法,因为 Java 8 中支持接口中可以有 default 关键字修饰的有默认实现的默认方法,这个默认的方法是可以不需要子类实现的,可使用@FunctionalInterface
注解来强制接口只能有一个抽象方法,只有一个抽象方法的接口称为函数式接口。
package com.chenpi;
/**
* @author 陈皮
* @version 1.0
* @description
* @date 2022/3/18
*/
public class ChenPi {
public static void main(String[] args) {
test((String name, int age) -> {
System.out.println("大家好我是" + name + ",今年" + age + "岁!");
});
}
public static void test(Human human) {
String name = "陈皮";
int age = 18;
human.speak(name, age);
}
}
@FunctionalInterface
interface Human {
void speak(String name, int age);
}
可以省略参数列表的参数类型,如下所示。
package com.chenpi;
/**
* @author 陈皮
* @version 1.0
* @description
* @date 2022/3/18
*/
public class ChenPi {
public static void main(String[] args) {
test((name, age) -> {
System.out.println("大家好我是" + name + ",今年" + age + "岁!");
});
}
public static void test(Human human) {
String name = "陈皮";
int age = 18;
human.speak(name, age);
}
}
@FunctionalInterface
interface Human {
void speak(String name, int age);
}
Lambda 体只有一行语句,可以省略大括号 {},并且末尾不能加 ; 分号,如下所示。
package com.chenpi;
/**
* @author 陈皮
* @version 1.0
* @description
* @date 2022/3/18
*/
public class ChenPi {
public static void main(String[] args) {
test((name, age) -> System.out.println("大家好我是" + name + ",今年" + age + "岁!"));
}
public static void test(Human human) {
String name = "陈皮";
int age = 18;
human.speak(name, age);
}
}
@FunctionalInterface
interface Human {
void speak(String name, int age);
}
如果参数列表只有一个参数,可以省略 () 括号,如下所示。
package com.chenpi;
/**
* @author 陈皮
* @version 1.0
* @description
* @date 2022/3/18
*/
public class ChenPi {
public static void main(String[] args) {
test(name -> System.out.println("大家好我是" + name));
}
public static void test(Human human) {
String name = "陈皮";
human.speak(name);
}
}
@FunctionalInterface
interface Human {
void speak(String name);
}
如果 Lambda 体有返回值,而且只有一行语句,{} 和 return 关键字都可以省略,并且语句末尾不能加 ; 分号,如下所示。
package com.chenpi;
/**
* @author 陈皮
* @version 1.0
* @description
* @date 2022/3/18
*/
public class ChenPi {
public static void main(String[] args) {
test(age -> age + 10);
}
public static void test(Human human) {
int age = 18;
int speakAge = human.speak(age);
System.out.println(speakAge);
}
}
@FunctionalInterface
interface Human {
int speak(int age);
}
与匿名内部类一样,Lambda 表达式也可以访问定义在 Lambda 体外面的变量。但对于局部变量,它也只能访问final
类型的变量,但与匿名内部类不同的是,它不要求变量声明为 final,只要不被重新赋值即可。
package com.chenpi;
/**
* @author 陈皮
* @version 1.0
* @description
* @date 2022/3/18
*/
public class ChenPi {
private static int AGE = 20;
public static void main(String[] args) throws InterruptedException {
int i = 10;
// 不能再重新赋值,不然会导致下面对i的使用报错
// i = 12;
// 不是局部变量可以修改
AGE = 21;
test((name, age) -> {
// 访问外部的变量
System.out.println(AGE);
// 访问局部变量
System.out.println(i);
System.out.println("大家好我是" + name + ",今年" + age + "岁!");
return age + i;
});
}
public static void test(Human human) {
int age = 18;
String name = "陈皮";
int speakAge = human.speak(name, age);
System.out.println(speakAge);
}
}
@FunctionalInterface
interface Human {
int speak(String name, int age);
}
// 输出结果如下
21
10
大家好我是陈皮,今年18岁!
28
原理和匿名内部类一样,外部局部变量的值作为参数传递给 Lambda 表达式,为 Lambda 表达式建立一个副本,Lambda 访问的是副本,而不是外部局部变量。如果外部变量允许修改,程序员可能会误以为 Lambda 表达式读到修改后的值,引起混淆。
为什么不直接访问外部的局部变量呢?因为外部局部变量定义在栈中,当 Lambda 表达式被执行的时候,外部局部变量可能早已被释放了。如果希望能够修改值,可以将变量定义为实例变量,或者将变量定义为数组。
package com.chenpi;
/**
* @author 陈皮
* @version 1.0
* @description
* @date 2022/3/18
*/
public class ChenPi {
public static void main(String[] args) throws InterruptedException {
int[] arr = {1, 2};
// 数组可以修改
arr[0] = 5;
test(() -> {
// 访问局部变量
return arr[0];
});
}
public static void test(Human human) {
int speakAge = human.speak();
System.out.println(speakAge);
}
}
@FunctionalInterface
interface Human {
int speak();
}
// 输出结果如下
5
Lambda 表达式和匿名内部类很相似,那它是不是语法糖,内部实现其实就是内部类呢?答案是否定的,Java 会为每个匿名内部类生成一个类,但 Lambda 表达式不会。Lambda 表达式通常比较短,如果为每个表达式生成一个类会生成大量的类,性能会受到影响。
预定义函数式接口
Lambda 表达式是对函数式接口的简单实现。函数式接口只能有一个抽象方法。但还可以有 default 关键字修饰的有默认实现的默认方法。默认方法是可以不需要子类实现的,@FunctionalInterface
注解可以强制接口只能有一个抽象方法。
Java 8 定义了大量的预定义函数式接口,用于常见类型的代码传递,这些函数定义在包java.util.function
下。以下简单列举几个。
// 判断输入是否满足条件
Predicate<T> # boolean test(T t)
// 转换,输入类型T,输出类型R
Function<T, R> # R apply(T t)
// 工厂方法
Supplier<T> # T get()
// 消费者
Consumer<T> # void accept(T t)
// 转换,输入类型T和U,输出类型R
BiFunction<T, U, R> # R apply(T t, U u)
// BiFunction特例,输入输出类型都一样
BinaryOperator<T> # R apply(T t, U u)
本次分享到此结束啦~~
如果觉得文章对你有帮助,点赞、收藏、关注、评论,您的支持就是我创作最大的动力!