我们都知道,人们不愿意使用那些很麻烦的功能或者概念。目前,当要把新的行为传递给filterApples方法的时候,你不得不声明好几个实现ApplePredicate接口的类,然后去实例化好几个只会提到一次的ApplePredicate对象。下面的程序总结了你目前看到的一切。这个真是非常的啰嗦,很费时间!

public class AppleHeavyWeightPericate implements ApplePredicate { //选出重的苹果
@Override
public boolean test(Apple apple) {
return apple.getWeight() > 150;
}
}
public class AppleGreenColorPerdicate implements ApplePredicate {//选择绿苹果
@Override
public boolean test(Apple apple) {
return "green".equals(apple.getColor());
}
}
public class Demo {
public static void main(String[] args) {
List inventory = Arrays.asList(new Apple("green", 80), new Apple("green", 155), new Apple("red", 120));
List greenApples = filterApple(inventory, new AppleGreenColorPerdicate());
List heavyWeightApples= filterApple(inventory, new AppleHeavyWeightPericate());
}
private static List filterApple(List inventory, ApplePredicate p) {
List result = new ArrayList<>();
for (Apple apple : inventory) {
if (p.test(apple)) {
result.add(apple);
}
}
return result;
}
}

费这么大劲儿真没必要,能不能做的更好呢?Java有一个机制称为匿名类,它可以让你同时声明和实例化一个类。它可以帮助你进一步改善代码,让它变得更加的简洁。

1.匿名类

匿名类和你熟悉的Java局部类(块中定义的类)差不多,但匿名类没有名字。它允许你同时声明并且实例化一个类。换句话说,它允许你随用随建。

(1).使用匿名类

下面的代码展示了如何通过创建一个匿名类实现ApplePredicate的对象,重写筛选的例子:

List redApples = filterApple(inventory, new ApplePredicate() {
@Override
public boolean test(Apple apple) {
return "red".equals(apple.getColor());
}
});
但匿名类还是不够好。它往往显得非常的笨重,因为占用了很多空间。比如
List redApples = filterApple(inventory, new ApplePredicate() {
@Override
public boolean test(Apple apple) {
return "red".equals(apple.getColor());
}
});
List greenApples = filterApple(inventory, new ApplePredicate() {
@Override
public boolean test(Apple apple) {
return "green".equals(apple.getColor());
}
});

上面两段代码中,除了人return不同之外,其余的代码都是相同的。

整体来说,啰嗦就不好。它让人不愿意使用语言的某种功能,因为编写和维护啰嗦的代码需要很多时间,而且代码也易读。好的代码应该是一目了然的。即使使用匿名类处理在某种程度上盖上了为一个接口声明好几个实体类的啰嗦问题,但是它仍然不能令人满意。

(2)使用Lambda表达式

上面的代码在java8里可以使用Lambda表达式重写为下面的样子

List result = filterApple(inventory, (Apple apple) ->"red".equals(apple.getColor()));

不得不承认这代码看上去比先前干净很多。这个很好,因为它看起来更像问题称述本身了。我们现在已经解决了啰嗦的问题了。

(3)将List类型抽象化

在通向抽象的路上,我们还可以更进一步。目前,filterApples方法还只适用于Apple。你还可以将List类型抽象化,从而超越你眼前要处理的问题

public interface ApplePredicate {
boolean test(T t);
}
public class Demo {
public static void main(String[] args) {
List inventory = Arrays.asList(new Apple("green", 80), new Apple("green", 155), new Apple("red", 120));
List result = filterApple(inventory, (Apple apple) ->"red".equals(apple.getColor()));
for(Apple apple : result){
System.out.println(apple);
}
}
private static List filterApple(List inventory, ApplePredicate p) {
List result = new ArrayList<>();
for (T t : inventory) {
if (p.test(t)) {
result.add(t);
}
}
return result;
}
}

现在你可以把filter方法用在了香蕉、橘子、Integer或是String的列表上了。酷不酷?你现在在灵活性和间接性之间找到了最佳的平衡点,这在Java8之前是不可能的

3.真实的例子

&empsp;你现在已经看到了,行为参数化是一个很有用的模式,它能够轻松的适应不断变化的需求。这种模式可以把一个行为(一段代码)封装起来,并通过传递和使用创建的行为将方法的行为参数化。这种做法类似于策略设计模式。

(1).使用Comparator来排序

对集合进行排序是一个常见的编程任务。比如,你的那位农民朋友想要根据苹果的重量对库存的进行排序,或者他可能改变了主意,希望你根据颜色对苹果进行排序,听起来怎么有点耳熟呢?是的,你需要一种方法来表示和使用不同的排序行为,来轻松的适应变化的需求。

在Java8中,List自带一个sort方法(你也可以使用Collections.sort)。sort的行为可以用Java.util.Comparator对象来进行参数化,它的接口如下:

public interface Comparator {
int compare(T o1, T o2);
}

因此,你可以随时创建Comparator的实现,用sort方法表现出不同的行为。比如,你可以使用匿名类,按照重量升序对库存进行排序。

inventory.sort(new Comparator() {
@Override
public int compare(Apple o1, Apple o2) {
return o1.compareTo(o2);
}
});

用Lambda表达式的话,看起来就是这样:

inventory.sort((Apple o1, Apple o2) ->o1.compareTo(o2));

(2).使用Runnable执行代码块

线程就像是轻量级的进程:它们自己执行一个代码块。但是怎么才能告诉线程要执行那块代码呢?多个线程可能会运行不同的代码。我们需要一种方式来代表稍后执行的一段代码。在Java里面,你可以使用Runnable接口表示一个要执行的代码块。请注意,代码不会返回任何结果(即void):

你可以像下面这样,使用这个接口创建执行不同行为的线程:

Thread t = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("hello world");
}
});
使用Lambda表达式,看起来是这样的:
Thread t = new Thread(()->System.out.println("hello world"));