Java 8 新特性 Lambda 表达式 与 函数式接口 详解
Java 8 新特性 Lambda表达式 与 函数式接口 详解
1、简介
1.1、什么是Lambda表达式?
Lambda表达式(闭包):java8的新特性,lambda运行将函数作为一个方法的参数,也就是函数作为参数传递到方法中。使用lambda表达式可以让代码更加简洁。
1.2、Lambda基础语法
Lambda 表达式在 Java 语言中引入了一个新的语法元素和操作符。这个操作符为 "->" ,该操作符被称为 Lambda 操作符或箭头操作符 。它将 Lmabda 分为两个部分:
- 左侧:指定了 Lambda 表达式需要的所有参数;方法的参数列表,要求和实现的接口中的方法参数部分⼀致,包括参数的数量和类型。
- 右侧:指定了 Lambda 体,即 Lambda 表达式要执行的功能。如果接口中定义的方法有返回值,则在实现的时候,注意返回值的返回。
1.2.1、参数的类型
由于在接口的方法中,已经定义了每⼀个参数的类型是什么。而且在使用lambda表达式实现接口的时候,必须要保证参数的数量和类 型需要和接口中的方法保持⼀致。因此,此时lambda表达式中的参数的类型可以省略不写。如果需要省略参数的类型,要保证:要省略, 每⼀个参数的类型都必须省略不写。绝对不能出现,有的参数类型省略了,有的参数类型没有省略。
1.2.2、参数的小括号
如果方法的参数列表中的参数数量 有且只有⼀个,此时,参数列表的小括号是可以省略不写的。
1.2.3、方法体部分的精简
方法体⼤括号的精简: 当⼀个方法体中的逻辑,有且只有⼀句的情况下,⼤括号可以省略。
1.2.4、return的精简
如果⼀个方法中唯⼀的⼀条语句是⼀个返回语句, 此时在省略掉大括号的同时, 也必须省略掉return。
1.2.5、函数的引用
lambda表达式是为了简化接口的实现的。在lambda表达式中,不应该出现比较复杂的逻辑。如果在lambda表达式中出现了过于复杂的逻辑,会对程序的可读性造成非常大的影响。如果在lambda表达式中需要处理的逻辑比较复杂,⼀般情况会单独的写⼀个方法。在lambda表达式中直接引用这个方法即可。
- 静态方法的引用: 语法: 类::静态方法。
- 非静态方法的引用:语法: 对象::非静态方法。
- 构造方法的引用:语法: 类名::new
public class TestLam {
public static void main(String[] args) {
//实现多个参数,一个返回值的接口
//对一个静态方法的引用,语法:类::静态方法
Test test = Calculator::calculate;
System.out.println(test.test(4,5));
}
}
class Calculator{
public static int calculate(int a,int b ){
// 稍微复杂的逻辑:计算a和b的差值的绝对值
if (a > b) {
return a - b;
}
return b - a;
}
}
interface Test{
int test(int a, int b);
}
1.3、什么是函数式接口?
函数式接口(Functional Interface)就是一个有且仅有一个抽象方法,但是可以有多个非抽象方法的接口。函数式接口可以被隐式转换为 lambda 表达式。为了确保你的接口一定达到这个要求,你只需要给你的接口添加 @FunctionalInterface 注解,编译器如果发现你标注了这个注解的接口有多于一个抽象方法的时候会报错的。需要注意的是,即使不使用该注解,只要满足函数式接口的定义,这仍然是一个函数式接口,使用起来都一样。
2、Java 内置四大核心函数式接口
函数式接口 | 参数类型 | 返回类型 | 用途 |
Consumer< T > 消费型接口 | T | void | 对类型为T的对象应用操作。包含方法:void accept(T t); |
Supplier< T > 供给型接口 | 无 | T | 返回类型为T的对象。包含方法:T get(); |
Function<T, R> 函数型接口 | T | R | 对类型为T的对象应用操作,并返回结果。结果是R类型的对象。包含方法:R apply(T t); |
Predicate< T > 断定型接口 | T | boolean | 确定类型为T的对象是否满足某约束,并返回boolean 值。包含方法boolean test(T t); |
其他接口
函数式接口 | 参数类型 | 返回类型 | 用途 |
BiConsumer<T, U> | T, U | void | 对类型为T, U 参数应用操作。包含方法为void accept(T t, U u) |
BiFunction<T, U, R> | T, U | R | 对类型为 T, U 参数应用操作, 返回 R 类型的结 果。包含方法为R apply(T t, U u); |
3、使用示例
3.1、Supplier接口
java.util.function.Supplier:生产数据。
- 抽象方法:
T get()
:用来获取一个泛型参数指定类型的对 象数据。
示例:求数组元素的最大值:
public static void main(String[] args) {
int[] arr = {3, 7, 45, 12, 90, 6};
int max = getMax(() -> {
int maxNum = arr[0];
for (int value : arr) {
if (value > maxNum) {
maxNum = value;
}
}
return maxNum;
});
System.out.println("最大值是:" + max);
}
public static int getMax(Supplier<Integer> sup) {
return sup.get();
}
3.2、Consumer接口
java.util.function.Consumer:消费一个数据, 其数据类型由泛型决定。
- 抽象方法: void accept(T t):消费一个指定泛型的数据。
- 默认方法: andThen() :消费数据的时候,首先做一个操作, 然后再做一个操作,实现组合。
示例: 下面的字符串数组当中存有多条信息,请按照格式“ 姓名:XX。性别:XX。 ”的格式将信息打印出来。要求将打印姓 名的动作作为第一个 Consumer 接口的Lambda实例,将打印性别的动作作为第二个 Consumer 接口的Lambda实 例,将两个 Consumer 接口按照顺序“拼接”到一起。
public static void main(String[] args) {
String[] arr = {"张三,男", "李四,男", "王五,男", "小学妹,男"};
printInfo(info -> System.out.print("姓名:" + info.split(",")[0] + ","),
info -> System.out.println("性别:" + info.split(",")[1] + "。"),
arr);
}
private static void printInfo(Consumer<String> one, Consumer<String> two, String[] arr) {
for (String s : arr) {
one.andThen(two).accept(s);
}
}
3.3、Predicate接口
java.util.function.Predicate:对某种类型的数据进行判断,返回boolean值。
抽象方法
boolean test(T t):对指定类型数据进行判断的方法,符合条件返回true,不符合返回false。
默认方法
- and() :与
- or() :或
- negate() :非
示例: 数组当中有多条“姓名+性别”的信息如下,请通过 Predicate 接口的拼装将符合要求的字符串筛选到集合 ArrayList 中,需要同时满足两个条件: 1. 必须为女生; 2. 姓名为4个字。
public static void main(String[] args) {
String[] array = { "迪丽热巴,女", "古力娜扎,女", "马尔扎哈,男", "赵丽颖,女" };
List<String> list = filter(s -> "女".equals(s.split(",")[1]),
s -> s.split(",")[0].length() == 4,
array);
System.out.println(list);
}
private static List<String> filter(Predicate<String> one, Predicate<String> two, String[] arr) {
ArrayList<String> list = new ArrayList<>();
for (String s : arr) {
if (one.and(two).test(s)) {
list.add(s);
}
}
return list;
}
3.4、Function接口
java.util.function.Function<T, R>:根据一个类型的数据得到另一个类型的数据,前者称为前置条件, 后者称为后置条件。
- 抽象方法: R apply(T t) :根据类型T的参数获取类型R的结果。
- 默认方法: andThen() :用来进行组合操作。
示例: 使用 Function 进行函数模型的拼接,按照顺序需要执行的多个函数操作为: String str = “小师妹,20”;
public static void main(String[] args) {
String str = "小师妹,20";
int age = getAge(s -> s.split(",")[1],
s -> Integer.parseInt(s),
s -> s += 100,
str);
System.out.println(age);
}
private static int getAge(Function<String, String> one, Function<String, Integer> two, Function<Integer, Integer> three, String str) {
return one.andThen(two).andThen(three).apply(str);
}