一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第23天,点击查看活动详情。
前言
我们在解析Java 行为参数化后,对Java行为参数化的概念有一定了解后,我们来继续分析之前的例子,通过ApplePredicate的各种各样的实现类后,我们不再惧怕用户的各种各样的筛选需求了。但是有些需求只是一时的,你要为客户多种多样的需求创建各种各样的ApplePredicate的实现,并且只是使用一次,这样你的项目会充斥着一大堆只使用一次的ApplePredicate的实现类。这时候,假如客户再想要筛选香蕉呢,橙子呢。那么如何来解决这些问题?
Lambda表达式使用说明
这时候我们需要超越眼前的问题,将List类型和ApplePredicate抽象化,我们可以定义如下的代码块:
public interface Predicate<T> {
boolean test(T t);
}
public static <T> List<T> filter(List<T> list, Predicate<T> predicate) {
List<T> resultList = new ArrayList<>();
for (T t : list) {
if (predicate.test(t)) {
resultList.add(t);
}
}
return resultList;
}
这种时候我们就可以使用Java 8引入的Lambda表达式的功能了,比如我先想要筛选红色的苹果或者是50克以上的橙子。具体代码如下:
// 筛选红色苹果
FilterTest.filter(apples, apple -> Apple.Color.RED.equals(apple.getColor()));
// 筛选50克以上的橙子
FilterTest.filter(oranges, orange -> orange.getWeight() > 50);
通过行为参数化的抽象以及我们的Lambda表达式的帮助下,我们可以轻松的将代码扩展到橙子,香蕉的筛选,甚至是我们的整数集合,单词集合的筛选。通过书写Lambda表达式,整行代码更加符合我们的思维逻辑,以上的第一行代码,我们阅读过去:筛选苹果集合,筛选苹果颜色是红色的苹果。清晰易懂,而且扩展性极强。
Lambda表达式的定义为可以理解为一种简洁的可传递的匿名函数:它没有名称,但是它有参数列表,函数主体,返回类型。可能还有一个可以抛出的异常列表。通过Lambda表达式,我们不需要像匿名类那样书写模板式的代码。Lambda有两种表达式风格:
- 表达式-风格Lambda (parameters)-> expression
- 块-风格Lambda parameters)-> { statements;} 从这两种风格中,我们可以看到Lambda表达式由三个部分组成。参数列表,箭头,函数主体,箭头负责将参数列表和函数主体分隔开。
其实在我们之前定义的ApplePrecate接口不是必需的,因为在java.util.function中,Java的开发者们已经帮我们定义了适用大多数情况的函数式接口了。具体的参见如下表格:
| 使用案例 | Lambda的例子 | 对应的函数式接口 |
|---|---|---|
| 布尔表达式 | (List list) -> list.isEmpty() | Predicate<List<String>> |
| 创建对象 | () -> new Apple(10) | Supplier |
| 消费一个对象 | (Apple a) -> System.out.println(a.getWeight()) | Consumer |
| 从一个对象中选择/提取 | (String s) -> s.length() | Function<String, Integer> 或者 ToIntFunction |
| 合并两个值 | (Int a, int b) -> a * b | IntBinaryOperator |
| 比较两个对象 | (Apple a1, Apple a2) ->a1.getWeight().compareTo(a2.getWeight()) | Comparator 或者 BiFunction<Apple, Apple, Integer> ToIntBiFunction<Apple, Apple> |
表格上已经举例子说明了每个函数式接口适应的一个场景。以及它的一个示例。
Predicate
java.util.function.Predicate接口定义了一个名为test的抽象方法,接受泛型的T对象,并返回boolean。与我们之前使用的ApplePredicate差不多的形式。
Consumer
java.util.function.Consumer接口定义了一个名叫accept的抽象方法,它接受泛型T的对象,没有返回(void)。我们如果需要对T类型的对象执行某些操作的时候,可以使用这个接口。比如,使用它来创建一个Foreach方法,接收一个Integers的列表。并对每个证书进行加2操作。或者打印整个整数集合。
Function
java.util.function.Function<T, R>接口定义了一个叫作apply的抽象方法,它接受泛型T的对象,并返回一个泛型R的对象。使用这个接口我们可以提取对象的某些有用的参数。比如苹果的重量,单词字符串的长度。
总结
java.util.function包中还有许多的函数式接口,但是大多数的接口都是表格中接口的变种,目的是为了节省资源,提高效率而产生的。那么,今天我们的Lambda表达式的学习就此完成了。