Java8新特性实战

作者: Ian | 2018-09-09 | 阅读

引言

之前有自我总结Java8新特性Java8之前版本的特性两篇 Java8相关的文章,Z公司里面用Java8 的新特性也用的多,越是深入这个越发现这个Lambda越强大,下面直接上例子,理论就不说了。

传递代码:一个例子

假设你有一个Apple类,它有一个getColor方法,还有一个变量inventory保存着一个Apples的列表。你可能想要选出所有的绿苹果,并返回一个列表。通常我们用筛选filter一词来表达这个概念。在Java8之前,你可能会写这样一个方法filterGreenApples:

public static List<Apple> filterGreenApples(List<Apple> inventory){
    /*
    result 用来积累结果List,开始为空,然后一个个加入绿苹果
    */
    List<Apple> result = new ArrayList<>();
    for(Apple apple : inventory){
        if("green".equals(apple.getColor())){
            result.add(apple);
        }
    }
    return result;
}

但是接下来,有新的需求可能需要选出重的苹果,比如超过150克,于是你心情沉重的写下了下面这个方法,甚至用了复制粘贴:

public static List<Apple> filterGreenApples(List<Apple> inventory){
    /*
    result 用来积累结果List,开始为空,然后一个个加入绿苹果
    */
    List<Apple> result = new ArrayList<>();
    for(Apple apple : inventory){
        if(apple.getWeight() > 150){
            result.add(apple);
        }
    }
    return result;
}

我们都知道软件工程中复制粘贴的危险– 给一个做了更新和修正,却忘了另一个。这个两个代码只有if里面的条件不同。如果这两个高亮的方法之间的差异仅仅是接受的重量范围不同,那么我们只需要把接受重量上下限作为参数传递给filter就行了,比如 指定(150,1000)来选出重的苹果(超过150克),或者指定(0,80)来选出轻的苹果(低于80克)。

但是Java8会把条件代码作为参数传递进去,这样可以避免filter方法出现重复的代码。现在可以这样写:

public static boolean isGreenApple(Apple apple){
    return "green".equals(apple.getColor());
}
public static boolean isHeavyApple(Apple apple){
    return apple.getWeight() > 150;
}
/*
写出来为了清晰,平常只要从java.util.function导入就可以了
*/
public interface Predicate<T>{
    boolean test(T t);
}

/*
方法作为Predicate参数p传递进去
*/
static List<Apple> filterApples(List<Apple> inventory, Predicate<Apple> p){
    List<Apple> result = new ArrayList<>();
    for(Apple apple : inventory){
        if(p.test(apple)){
            result.add(apple);
        }
    }
    return result;
}

要用它的话,可以这样写:

filterApples(inventory, Apple::isGreenApple);

//或者

filterApples(inventory, Apple::isHeavyApple);

从传递方法到Lambda

把方法作为值来传递明显很有用,但要是为类似于isHeavyApple和isGreenApple这种可能只用一两次的短方法写一堆定义有点烦人。不过Java 8 也解决了这个问题,它引入了一套新记法(匿名函数或Lambda),可以这样写:

filterApples(inventory, (Apple a) -> "green".equals(a.getColor()));

//或者
filterApples(inventory, (Apple a) -> a.getWeight() > 150);

//甚至
filterApples(inventory, (Apple a) -> a.getWeight() < 80 || "brown".equals(a.getColor()));

所以,你甚至都不需要为只用一次的方法写定义;代码更干净、更清晰,因为你用不着去找自己到底传递了什么代码。但要是Lambda的长度多于几行(它的行为也不是一目了然)的话,那你还是应该用方法引用来指向一个描述名称的方法,而不是使用匿名的Lambda。你应该以代码的清晰度为准绳。

Java 8 的设计师几乎可以就此打住了,要是么有多核CPU,可能他们真的就到此为止了。我们迄今为止谈到的函数式编程竟然如此强大,其实,java加上filter和几个相关的东西作为通用库方法就足以让人满意了,比如:

static <T> Collection<T> filter(Collection<T> c, Predicate<T> p);

// 这样你甚至都不需要写filterApples了,因为比如先前的调用
filterApples(inventory, (Apple a) -> a.getWeight() > 150);

//可以直接调用库方法filter:
filter(inventory, (Apple a) -> a.getWeight() > 150)

但是,为了更好的利用并行,Java的设计师没有这么做。

多核、并行

所有新的台式和笔记本电脑都是多核的,他们不是仅有一个CPU,而是四个、八个,甚至更多CPU,通常称为内核。问题是,经典的Java程序员只能利用其中一个核,其它核的处理能力都浪费了。

后面会持续更新《Java8性特性实战》。。。。


版权声明:本文由 Ian 在 2018年09月09日发表。本文采用CC BY-NC-SA 4.0许可协议,非商业转载请注明出处,不得用于商业目的。
文章题目及链接:《Java8新特性实战》




  相关文章:


留言区:

TOP