目录
序言
接触Java8一段时间了,发现这东西确实很好,公司许多大牛也都在用,买了一本书《Java8实战》,阅读书中的一些内容做点学习笔记。
初试牛刀:筛选绿苹果
第一个解决的问题可能是下面这样的:
public static List<Apple> filterGreenApples(List<Apple> inventory){
List<Apple> result = new ArrayList<Apple>();
for(Apple apple: inventory){
if("greeen".equals(apple.getColour())){
result.add(apple);
}
}
return result;
}
上面这个是我们要筛选出绿苹果的程序,需求是在时刻变化的嘛!现在农民改变主意了,想要筛选出其他颜色的苹果,如浅绿色、暗红色、黄色等。那我们首先想到的是将其抽象化。
再展身手:把颜色作为参数
当需求变更时,我们首先想到的是将颜色作为参数,然后灵活的适应变化:
public static List<Apple> filterGreenApples(List<Apple> inventory, String colour){
List<Apple> result = new ArrayList<Apple>();
for(Apple apple: inventory){
if(colour.equals(apple.getColour())){
result.add(apple);
}
}
return result;
}
这下就可以通过颜色来筛选出符合要求的苹果啦,那么现在农民需要筛选出重量大于150g的苹果,该怎么实现呢?你可能会想到这样来实现:
public static List<Apple> filterGreenApples(List<Apple> inventory, long weight){
List<Apple> result = new ArrayList<Apple>();
for(Apple apple: inventory){
if(apple.getWeight() > weight){
result.add(apple);
}
}
return result;
}
这样确实也满足了需求,但是复制了大量的代码,不符合软件的工程原则,那么你可以增加一个标志位的方式来区分颜色和重量的查询。于是你想到了下面的这种方式:
第三次尝试:对你想到的每一个属性做筛选
public static List<Apple> filterGreenApples(List<Apple> inventory, String colour, long weight, boolean flag){
List<Apple> result = new ArrayList<Apple>();
for(Apple apple: inventory){
if((flag&&apple.getWeight() > weight)||
(!flag&&apple.getColour().equals(colour))){
result.add(apple);
}
}
return result;
}
这样你就可以通过一个方法来控制两个属性的筛选了,是不是感到很机智?但是这让程序看起来很笨拙,如果农民需要筛选出大小、形状、产地。怎么办呢?在下一节,我们将利用行为参数化来实现这种灵活性。
行为参数化
我们首先想到的是定一个接口来对选择标准进行建模:
interface ApplePredicate{
boolean test(Apple apple);
}
class AppleHeavyWeightPredicate implements ApplePredicate{
public boolean test(Apple apple) {
return apple.getWeight() >150;
}
}
class AppleGreenColorPredicate implements ApplePredicate{
public boolean test(Apple apple) {
return "green".equals(apple.getColour());
}
}
上面所做的操作和“策略设计模式”相关,让方法接收多种行为作为参数,并在内部使用来完成不同的行为。
根据抽象条件筛选
public class GreenApple {
public static List<Apple> filterApples(List<Apple> inventory, ApplePredicate p){
ArrayList<Apple> result = new ArrayList<>();
for(Apple apple: inventory){
if(p.test(apple)){
result.add(apple);
}
}
return result;
}
public static class AppleRedAndHeavyPredicate implements ApplePredicate{
@Override
public boolean test(Apple apple) {
return "red".equals(apple.getColour())&&apple.getWeight() > 150;
}
}
public static void main(String[] args) {
Apple apple = new Apple();
apple.setColour("green");
apple.setWeight(12);
Apple apple2 = new Apple();
apple2.setColour("red");
apple2.setWeight(288);
ArrayList<Apple> inventory = new ArrayList<Apple>();
inventory.add(apple);
inventory.add(apple2);
List<Apple> apples = GreenApple.filterApples(inventory, new AppleRedAndHeavyPredicate());
}
}
通过这种方式,农民只需创建一个类来实现ApplePredicate接口就行了,你可以在这个类里面定义你想筛选的规则,这样就可以将filterApples方法的行为参数化了。
匿名内部类
List<Apple> redApples = filterApples(inventory, new ApplePredicate() {
@Override
public boolean test(Apple apple) {
return "red".equals(apple.getColour());
}
});
虽然这样也可以,但是会显的程序很笨拙
使用Lambda表达式
使用Lambda表达式会让程序看起来很干净整洁
List<Apple> result=filterApples(inventory,(Apple apple1) ->"red".equals(apple1.getColour()));
甚至可以省略Apple类型,因为它只有一个方法
List<Apple> result=filterApples(inventory,apple1 ->"red".equals(apple1.getColour()));
将List类型抽象化
在通往抽象的路上,其实我们可以做到更好!你可以将List类型抽象化,从而超越你目前想处理的问题
List<Apple> redApple = filter(inventory, (Apple apple) -> "red".equals(apple.getColour()));
List<String> str = filter(numbers, (Integer i) -> i % 2 == 0);
public static <T>List<T> filter(List<T> list, Predicate<T> p){
List<T> result = new ArrayList<>();
for(T e:list){
if(p.test(e)){
result.add(e);
}
}
return result;
}
小结
行为参数化:就是一个方法接受多个不同的行为作为参数,并在内部使用它们,完成不同行为的能力
传递代码:就是将新行为作为参数传递给方法