3.1 Lambda 管中窥豹
可以把Lambda表达式理解为简洁地表示可传递的匿名函数的一种方式:它没有名称,但它有参数列表、函数主体、返回类型,可能还有一个可以抛出的异常列表利用Lambda表达式,可以更为简洁地自定义一个Comparator对象。
// 先前
Comparator<Apple> byWeight = new Comparator<Apple>() {
public int compare(Apple a1, Apple a2){
return a1.getWeight().compareTo(a2.getWeight());
}
};
// 之后
Comparator<Apple> byWeight =
(Apple a1, Apple a2) -> a1.getWeight().compareTo(a2.getWeight());
参数列表——这里它采用了Comparator中compare方法的参数,两个Apple。
箭头——箭头->把参数列表与Lambda主体分隔开。
Lambda主体——比较两个Apple的重量。表达式就是Lambda的返回值了。
3.2 在哪里以及如何使用 Lambda
可以在上一章中实现的filter方法中使用Lambda可 以 把 Lambda 表 达 式 作 为 第 二 个 参 数 传 给 filter 方 法因 为 它 这 里 需 要Predicate,而这是一个函数式接口
List<Apple> greenApples =filter(inventory, (Apple a) -> "green".equals(a.getColor()))
3.2.1 函数式接口
它就是一个函数式接口!因为Predicate仅仅定义了一个抽象方法 函数式接口就是只定义一个抽象方法的接口
public interface Predicate<T>{
boolean test (T t);
}
3.3 使用函数式接口
3.3.1 Predicate
接口定义了一个名叫test的抽象方法,它接受泛型T对象,并返回一个boolean。
public interface Predicate<T>{
boolean test(T t);
}
public static <T> List<T> filter (List<T> list, Predicate<T> p){
List<T> results = new ArrayList<>();
for (T s : list){
if(p.test(s)){
results.add(s);
}
}
return results;
}
List<Apple> appleList = new ArrayList<>();
// 筛选重量等于1得苹果
filter(appleList,(Apple a)->a.getWeight() == 1 );
// 筛选红色得苹果
filter(appleList,(One.Apple a)->a.getColor().equals("red"));
3.3.2 Consumer
定义了一个名叫accept的抽象方法它接受泛型T的对象,没有返回( void)。你如果需要访问类型T的对象,并对其执行某些操作,就可以使用这个接口
public static <T> void filter (List<T> list, Consumer<T> p){
for (T i : list){
p.accept(i);
}
}
filter(appleList,(Apple i)-> System.out.println(i));
3.3.3 Function
定义了一个叫作apply的方法,它接受一个泛型T的对象,并返回一个泛型R的对象
public static <T,R> List<R> map (List<T> list, Function<T,R> f){
List<R> result = new ArrayList<>();
for (T s : list){
result.add(f.apply(s));
}
return result;
}
List<Integer> map = map(Arrays.asList("1", "2"), (String s) -> s.length());
3.3.4 原始类型特化
在Java里有一个将原始类型转换为对应的引用类型的机制。这个机制叫作装箱( boxing)。相反的操作,也就是将引用类型转换为对应的原始类型,叫作拆箱( unboxing)。 但这在性能方面是要付出代价的。装箱后的值本质上就是把原始类型包裹起来,并保存在堆里。因此,装箱后的值需要更多的内存,并需要额外的内存搜索来获取被包裹的原始值
Java 8为我们前面所说的函数式接口带来了一个专门的版本,以便在输入和输出都是原始类
型时避免自动装箱的操作。
3.4 Lambda 和方法引用实战
用不同的排序策略给一个Apple列表排序
3.4.1 传递代码
Java 8的API已经为你提供了一个List可用的sort方法sort方法的签名是这样的
void sort(Comparator<? super E> c)
第一个解决方案
List<Apple> appleList = new ArrayList<>();
public class AppleComparator implements Comparator<Apple>{
public int compare(Apple a1, Apple a2){
return a1.getWeight().compareTo(a2.getWeight());
}
}
appleList .sort(new AppleComparator());
3.4.2 使用匿名内部类
appleList.sort(new Comparator<Apple>() {
@Override
public int compare(Apple o1, Apple o2) {
return o1.getWeight().compareTo(o2.getWeight());
}
});
3.4.3 使用lambda表达式
appleList.sort((One.Apple o1, One.Apple o2)->o1.getWeight().compareTo(o2.getWeight()));
Java编译器可以根据Lambda出现的上下文来推断Lambda表达式参数的类型。那么你的解决方案就可以重写成这样:
appleList.sort((o1,o2)->o1.getWeight().compareTo(o2.getWeight()));
3.4.4 使用方法引用
import static java.util.Comparator.comparing;
appleList.sort(comparing(Apple::getWeight));
3.5 复合Lambda表达式
3.5.1 比较复合器
逆序
appleList.sort(comparing(Apple::getWeight).reversed());
比较链
如果发现有两个苹果一样重怎么办?哪个苹果应该排在前面呢?比如,在按重量比较两个苹果之后,你可能想要按颜色排序
appleList.sort(comparing(Apple::getWeight)
.reversed()
.thenComparing(Apple::getColor));
3.5.2 谓词复合
谓词接口包括三个方法: negate、 and和or,让你可以重用已有的Predicate来创建更复
杂的谓词
产生现有Predicate对象redApple的非
public static class ColorPredicate implements Predicate<Apple> {
@Override
public boolean test(Apple apple) {
return false;
}
}
ColorPredicate colorPredicate = new ColorPredicate();
Predicate<Apple> negate = colorPredicate.negate();
链接两个谓词来生成另一个Predicate对象
Predicate<Apple> and = negate.and(a -> a.getWeight() > 150);
链接Predicate的方法来构造更复杂Predicate对象
Predicate<Apple> and = negate
.and(a -> a.getWeight() > 150)
.or(a->a.getColor().equals("green"));