引言
之前有自我总结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性特性实战》。。。。