Java8 中新增加了对于 Lambda 表达式的支持,还添加了 Stream 接口,便于对集合对象功能的增强。能够极大的简化代码的编写并提高代码的可读性,提供的
并发编程模式能够充分利用多核计算,而且无需编写线程相关的代码。下面的内容将会给出使用新的 Lambda 语法对集合对象进行扩展使用。
什么是函数式接口?
以最常用的迭代操作来举例,在 Java8 之前,我们进行迭代一个集合需要这样做:
List numbers = Arrays.asList(100, 200, 300, 400);
for (Integer number : numbers) {
System.out.println(number);
}
升级到 Java8 之后,使用 Lambda 表达式进行迭代:
numbers.forEach(number -> System.out.println(number));
甚至可以进一步简化代码:
numbers.forEach(System.out::println);
那么这个 forEach
方法是什么?为什么可以做到这一点呢?查看源码,发现 forEach
方法是来自 Iterable
接口的一个默认方法。它的源码如下:
default void forEach(Consumer super T> action) {
Objects.requireNonNull(action);
for (T t : this) {
action.accept(t);
}
}
阅读源码不难发现,实际上 forEach
还是通过普通的 for
循环实现的。只不过它的方法签名需要传递一个 Consumer
接口。我们来看一下这个接口的代码:
@FunctionalInterface
public interface Consumer {
void accept(T t);
}
@FunctionalInterface
注解声明了该接口是一个函数式接口,这一类函数式接口有且仅有一个方法,如果添加多个方法,会出现编译错误。我们将一个 Lambda 表达式
传递给方法时,编译器会将 Lambda 表达式转换成对应接口的匿名实现类。也就是说传递给 forEach
方法的是 Consumer
的匿名实现类。为了便于观察,
我们可以将 Lambda 展开成匿名类的写法:
List numbers = Arrays.asList(100, 200, 300, 400);
Consumer consumer = new Consumer() {
@Override
public void accept(Integer number) {
System.out.println(number);
}
};
numbers.forEach(consumer);
如何应用函数式接口?
java.util.function
包中定义了一系列和函数式编程相关的类与接口,针对基本数据类型的接口有以下几种:
- Predicate -- 传入一个参数,返回一个 boolean 结果, 方法为 `boolean test(T t)``
- Consumer -- 传入一个参数,无返回值,纯消费。 方法为 `void accept(T t)``
- Function -- 传入一个参数,返回一个结果,方法为 `R apply(T t)``
- Supplier -- 无参数传入,返回一个结果,方法为
T get()
- UnaryOperator -- 一元操作符, 继承 Function,传入参数的类型和返回类型相同。
- BinaryOperator -- 二元操作符, 传入的两个参数的类型和返回类型相同,继承 BiFunction
我们可以使用这些接口实现一些自己的函数式接口,还是以迭代为例,我们来自己实现一个 forEach
方法:
// 定义一个函数式接口 MyConsumer,接收一个泛型参数无返回值
@FunctionalInterface
public interface MyConsumer {
void accept(T t);
}
// 定义一个自己的 forEach 方法,传入自己定义的函数式接口和需要迭代的集合
public class Demo {
public static void forEach(MyConsumer consumer, Collection collection) {
for (T t : collection) {
consumer.accept(t);
}
}
}
// 调用自己的 forEach 方法,打印集合元素
public static void main(String[] args) {
List numbers = Arrays.asList(100, 200, 300);
Demo.forEach(System.out::println, numbers);
}