前面 Java 8之方法引用和Lambda表达式这篇文章讲了方法引用和Lambda表达式的大致原理和使用方法,但是光有理论是不够的,现在这篇文章会讲到它们的实际应用,从最开始的需求一步步的优化代码,直到最后使用Lambda表达式。
筛选苹果:
我们现在有个Apple类,它有weight和color属性分别代表它的重量和属性,我们创建多个苹果放入List中,这样就有了很多不同的苹果。
public class Apple {
private String color;
private Integer weight;
public Apple(String color, Integer weight) {
this.color = color;
this.weight = weight;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
public Integer getWeight() {
return weight;
}
public void setWeight(Integer weight) {
this.weight = weight;
}
@Override
public String toString() {
return "Apple{" +
"color='" + color + '\'' +
", weight=" + weight +
'}';
}
}
public static void main(String[] args) {
List<Apple> apples = new ArrayList<Apple>();
Apple apple1 = new Apple("red", 100);
Apple apple2 = new Apple("green", 200);
Apple apple3 = new Apple("red", 300);
Apple apple4 = new Apple("red", 150);
apples.add(apple1);
apples.add(apple2);
apples.add(apple3);
apples.add(apple4);
}
-
假设我们现在有个Apple类,它有weight和color属性,现在有个需求,把红色的苹果挑出来,我们就写出了下列的代码。下面这段代码再常见不过了,把未分类的集合传进来,新建一个集合把筛选出来的元素放进去再返回,但是如果需求说要把绿色的都筛选出来呢?难道复制一个吗?那也太傻了,所以我们需要对代码进行改进。
public static List<Apple> filterGreenApples(List<Apple> apples) { List<Apple> result = new ArrayList<Apple>(); for (Apple apple : apples) { if ("green".equals(apple.getColor())) { result.add(apple); } } return result; }
-
经过我们的改进,把要筛选的颜色也当作参数传进来方法,这样想选什么样颜色就选什么颜色,但是需求永远都是会变得,如果说除了颜色还要再加上重量作为筛选呢?所以我们又对代码进行了改进。
public static List<Apple> filterApplesByColor(List<Apple> apples, String color) { List<Apple> result = new ArrayList<Apple>(); for (Apple apple : apples) { if (apple.getColor().equals(color)) { result.add(apple); } } return result; }
-
既然要加多重量作为筛选条件,那干脆把重量也当作参数传进去,这个时候我们发现这段代码开始变得很恶心了,在项目很赶的时候很多人会写出类似这种代码,需求是实现了,但是几乎没有什么阅读性可言,而且最多过一个星期,连你自己都可能看不懂自己写的什么东西,而且如果代码没有BUG也就算了,有了隐藏BUG在中途被发现再改的话是越改越乱,所以肯定不能用这样的代码,我们得再改进代码。
public static List<Apple> filterApples(List<Apple> apples, String color, int weight, boolean flag) { List<Apple> result = new ArrayList<Apple>(); for (Apple apple : apples) { if ((flag && apple.getColor().equals(color)) || (!flag && apple.getWeight() > weight)) { result.add(apple); } } return result; }
-
现在封装方法的方式已经没办法很好的解决问题了,所以我们决定使用设计模式中的策略模式解决这个问题,我们新建一个接口,里面定义一个方法接受
Apple
参数,然后我们只要实现这个接口重写这个方法,就可以在这个方法里面自定义我们的筛选代码了,我们可以直接用匿名类省去创建类的步骤,这也是最常用的方法,比如新建线程传入Runnable接口就是这样的做法public interface ApplePredicate { boolean test(Apple apple); } public class AppleHeavyWeightPredicate implements ApplePredicate { public boolean test(Apple apple) { return apple.getWeight() > 150; } } public static List<Apple> filterApples(List<Apple> apples, ApplePredicate p) { List<Apple> result = new ArrayList<Apple>(); for (Apple apple : apples) { if (p.test(apple)) { result.add(apple); } } return result; } //使用匿名类 List<Apple> redApples = filterApples(apples, new ApplePredicate() { public boolean test(Apple apple) { return "red".equals(apple.getColor()); } });
-
到现在其实我们的代码已经优化的很好了,实际上Java 8以前很多类库也都是这样实现的,但是这样的做法也有它的问题,那就是一长串的模板代码,阅读性也不怎么好,所以现在要轮到我们的主角上场了,使用Lambda表达式来优化。
List<Apple> result = filterApples(apples, (Apple apple) -> "red".equals(apple.getColor()));
-
到这里我们其实我们的目的已经实现了,但是我们还可以再改进代码,使这个方法不局限与Apple这个里,只需要加入泛型,就可以成为一个公用的筛选方法了,筛选任意的类型数据。我们把接口接受的参数类型改成泛型,返回的类型也改成泛型,接受的需要筛选的目标集合也改成泛型,这样就是一个可以筛选任何类型的公用方法了。
public interface Predicate<T> { boolean test(T t); } 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; }
Java自带类库使用Lambda表达式:
实际上Java 8中很多自带的类库已经可以使用Lambda表达式来调用了,就和我们上面的代码一样。
-
在Java 8中
List
自带了一个sort
方法,接受Comparator
(排序)类型参数,按照原来的方式就是是用匿名类,现在我们直接用Lambda表达式。public interface Comparator<T> { public int compare(T o1, T o2); } //匿名类使用方式 apples.sort(new Comparator<Apple>() { public int compare(Apple a1, Apple a2){ return a1.getWeight().compareTo(a2.getWeight()); } }); //Lambda表达式 apples.sort((Apple a1, Apple a2) -> a1.getWeight().compareTo(a2.getWeight()));
-
同理线程也是可以这样进行改造
public interface Runnable{ public void run(); } //匿名方法 Thread t = new Thread(new Runnable() { public void run(){ System.out.println("Hello world"); } }); //Lambda表达式 Thread t = new Thread(() -> System.out.println("Hello world"));
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。