一、函数接口
二、Lambda 表达式和匿名内部类
三、Lambda 表达式和集合
四、默认方法
五、其他
回到顶部
一、函数接口
接口 参数 返回类型 描述
Predicate
T boolean 用来比较操作
Consumer T void 没有返回值的函数
Function T R 有返回值的函数
Supplier None T 工厂方法-返回一个对象
UnaryOperator T T 入参和出参都是相同对象的函数
BinaryOperator (T,T) T 求两个对象的操作结果
为什么要先从函数接口说起呢?因为我觉得这是 java8 函数式编程的入口呀!每个函数接口都带有 @FunctionalInterface 注释,有且仅有一个未实现的方法,表示接收 Lambda 表达式,它们存在的意义在于将代码块作为数据打包起来。
没有必要过分解读这几个函数接口,完全可以把它们看成普通的接口,不过他们有且仅有一个抽象方法(因为要接收 Lambda 表达式啊)。
@FunctionalInterface 该注释会强制 javac 检查一个接口是否符合函数接口的标准。 如果该注释添加给一个枚举类型、 类或另一个注释, 或者接口包含不止一个抽象方法, javac 就会报错。
回到顶部
二、Lambda 表达式和匿名内部类
先来复习一下匿名内部类的知识:
如果是接口,相当于在内部返回了一个接口的实现类,并且实现方式是在类的内部进行的;
如果是普通类,匿名类相当于继承了父类,是一个子类,并可以重写父类的方法。
需要特别注意的是,匿名类没有名字,不能拥有一个构造器。如果想为匿名类初始化,让匿名类获得一个初始化值,或者说,想使用匿名内部类外部的一个对象,则编译器要求外部对象为final属性,否则在运行期间会报错。
new Thread(new Runnable() {
@Override
public void run() {
System.out.println(123);
}
}).start();
new Thread(()-> System.out.println(123)).start();
如上,和传入一个实现某接口的对象不同, 我们传入了一段代码块 —— 一个没有名字的函数。() 是参数列表, 和上面匿名内部类示例中的是一样的。 -> 将参数和 Lambda 表达式的主体分开, 而主体是之后操作会运行的一些代码。
Lambda 表达式简化了匿名内部类的写法,省略了函数名和参数类型。即参数列表 () 中可以仅指定参数名而不指定参数类型。
Java 是强类型语言,为什么可以不指定参数类型呢?这得益于 javac 的类型推断机制,编译器能够根据上下文信息推断出参数的类型,当然也有推断失败的时候,这时就需要手动指明参数类型了。javac 的类型推断机制如下:
对于类中有重载的方法,javac 在推断类型时,会挑出最具体的类型。
如果只有一个可能的目标类型, 由相应函数接口里的参数类型推导得出;
如果有多个可能的目标类型, 由最具体的类型推导得出;
如果有多个可能的目标类型且最具体的类型不明确, 则需人为指定类型。
回到顶部
三、Lambda 表达式和集合
java8 在 java.util 包中引入了一个新的类 —— Stream.java。java8 之前我们迭代集合,都只能依赖外部迭代器 Iterator 对集合进行串行化处理。而 Stream 支持对集合顺序和并行聚合操作,将更多的控制权交给集合类,是一种内部迭代方式。这有利于方便用户写出更简单的代码,明确要达到什么转化,而不是如何转化。
Stream 的操作有两种,一种是描述 Stream ,如 filter、map、peek 等最终不产生结果的行为称为"惰性求值";另外一种像 foreach、collect 等是从 Stream 中产生结果的行为称为"及早求值"。
接下来让我们瞧瞧 Stream 如何结合 Lambda 表达式优雅的处理集合...
1、及早求值
收集器:一种通用的、从流生成复杂值的结构。只要将它传给 collect 方法, 所有的流就都可以使用它了。在 java.util.stream.Collectors 中提供了一些有用的收集器。比如 toList、toSet、toMap 等。
在一个有序集合中创建一个流时,流中的元素就按出现顺序排列;如果集合本身就是无序的,由此生成的流也是无序的。需要注意的是,forEach 方法不能保证元素是按顺序处理的,如果需要保证按顺序处理,应该使用forEachOrdered 方法。当然,我们可以使用 sorted 方法对Stream 中的元素进行自定义排序。
foreach/forEachOrdered - 迭代集合
list.forEach(e -> System.out.println(e));
map.forEach((k, v) -> {
System.out.println(k);
System.out.println(v);
});
对 Stream 进行自定义排序
List collectSort = collect.stream().sorted(Comparator.comparing(String::length)).collect(Collectors.toList());
allMatch、anyMatch、noneMatch - 检查元素是否匹配
private boolean isPrime(int number) {
return IntStream.range(2, number)
.allMatch(x -> (number % x) != 0);
}
collect(toList()) - 由Stream里的值生成一个 List/Set/自定义 集合
List list = Stream.of("java", "C++", "Python").collect(Collectors.toList());
等价于:
List asList = Arrays.asList("java", "C++", "Python");
Set set = Stream.of("java", "python", "php").collect(Collectors.toSet());
TreeSet treeSet = Stream.of("java", "python", "php").collect(Collectors.toCollection(() -> new TreeSet<>()));
collect(toMap()) - 由Stream里的值生成一个 Map 集合
使用 toMap() 需要注意的是:Map中的key不能重复,如果重复的话,会抛出异常,因为 JVM 弄不清楚我是用新的Value、还是要用旧的Value呢?所以代码写成了如下的样子~~
Map strMap = Stream.of("java", "python", "php").collect(Collectors.toMap(String::new, String::new, (oldValue,newValue) -> oldValue));
如上使用 toMap() 仍然会有一个问题,就是 toMap 转化的时候,如果 value 为 null,会报一个 NullPointerException ,可用如下方式解决:
Map