一、基础知识
1)为什么要学习 Java8
Java 8 所做的改变,在许多方面比Java 历史上任何一次改变都更加深远,这些改变会让你的编程更加容易
例子:
传统写法:
List<Person> personList = Arrays.asList(new Person(21,50),new Person(22,55),new Person(23,60));
Collections.sort(personList, new Comparator<Person>() {
@Override
public int compare(Person o1, Person o2) {
return o1.getWeight().compareTo(o2.getWeight());
}
});
复制代码
Java 8写法:
personList.sort(Comparator.comparing(Person::getWeight));
复制代码
熟悉 Linux 操作的同学对这个指令应该不默认:
cat testFile | tr "[A-Z]" "[a-z]" | sort | tail -3
这种操作便是基于流式操作,cat
会把文件转换创建成一个流,然后tr
会转换流中字符,sort
会对流中的行进行排序,tail -3
则会输出流的最后三行。这种就像是流水线操作,经过每个中转站,将处理完的结果转入下一个处理中心,最后得到最终结果。
Java 8 的第一个编程思想就是流处理,流式一系列数据项,一次只生成一项,程序可以从输入流中一个一个读取数据项,然后以同样的方式将数据项写入输出流。一个程序的输出流很可能就是另一个程序的输入流。
函数传递
已知一个集合中有以下几种花:
List<Flower> flowerList = Arrays.asList(new Flower("red", 6), new Flower("yellow", 7), new Flower("pink", 8));
复制代码
这个时候如果我想要红花,那么传统写法是这样子的:
List<Flower> resList = new ArrayList<>();
for (Flower flower : flowerList) {
if (StringUtils.equals("red", flower.getColor())) {
resList.add(flower);
}
}
复制代码
那么如果我想要8块钱以下的花,那么写法就是这样的:
List<Flower> resList = new ArrayList<>();
for (Flower flower : flowerList) {
if (flower.getPrice() < 8) {
resList.add(flower);
}
}
复制代码
其实代码写法大部分都是一样的,只是判断的条件不一样,那么我们进行第一版优化:
我们将判断方法抽取出来:
public static boolean isRed(Flower flower) {
return StringUtils.equals("red", flower.getColor());
}
public static boolean isLowPrice(Flower flower) {
return flower.getPrice() < 8;
}
复制代码
借助函数式接口Predicate
,将我们自定义的方法传递进去:
public static List<Flower> filterFlower(List<Flower> flowers, Predicate<Flower> p) {
List<Flower> resList = new ArrayList<>();
for (Flower flower : flowers) {
if (p.test(flower)) {
resList.add(flower);
}
}
return resList;
}
复制代码
使用:
filterFlower(flowerList,Flower::isRed);
filterFlower(flowerList,Flower::isLowPrice);
复制代码
我们也可以借助 Lambda
流来传递函数,就可以不用事先写好判断函数了:
filterFlower(flowerList, (Flower f) -> StringUtils.equals("red", f.getColor()));
filterFlower(flowerList, (Flower f) -> f.getPrice() < 8);
复制代码
默认方法
在 Java 8 之前我们可以实现一个接口然后被强制重写这个接口的方法,那么隐含着很多问题:如果动物类新增一个fly()
方法,那么其实dog
这个类是不需要fly
这个方法的,但是如果不重写就会编译报错。因此在 Java 8 之后也设计了默认方法这一种方式巧妙的解决了这种问题。
interface Animal {
void eat();
void fly();
}
class bird implements Animal {
@Override
public void eat() {}
@Override
public void fly() {
System.out.println("bird fly");
}
}
class dog implements Animal {
@Override
public void eat() {}
}
复制代码
Java 8 之后可以这样写:
interface Animal {
void eat();
default void fly() {
System.out.println("animal fly");
}
}
class bird implements Animal {
@Override
public void eat() {}
@Override
public void fly() {
System.out.println("bird fly");
}
}
class dog implements Animal {
@Override
public void eat() {}
}
复制代码
以上便是 Java 8 的部分特性,那么接下来就让我们来了解 Java 8的使用
2)行为参数化
开发中,我们需要应对不断的需求,怎么样才能做到自适应可扩展就是我们要关注的地方。
需求1:筛选出红色的花
public static List<Flower> filterFlower(List<Flower> flowers) {
List<Flower> resList = new ArrayList<>();
for (Flower flower : flowers) {
if (StringUtils.equals("red", flower.getColor())) {
resList.add(flower);
}
}
}
复制代码
需求2:筛选出绿色的话
聪明的你肯定想到了我们可以通过传递一个颜色参数来过滤花朵,而不用每一次都修改主要代码。
public static List<Flower> filterFlowerByColor(List<Flower> flowers, String color) {
List<Flower> resList = new ArrayList<>();
for (Flower flower : flowers) {
if (StringUtils.equals(color, flower.getColor())) {
resList.add(flower);
}
}
}
复制代码
需求3:筛选出价格小于8块钱的花
这样子我们只能再写一个方法来实现这个需求,为了防止后续价格的变化,聪明的我们提前将价格设置成可变参数。
public static List<Flower> filterFlowerByPrice(List<Flower> flowers, Integer price) {
List<Flower> resList = new ArrayList<>();
for (Flower flower : flowers) {
if (flower.getPrice() < price) {
resList.add(flower);
}
}
}
复制代码
为了保持代码的整洁,我们被迫重写了一个方法来实现上述的需求:
public static List<Flower> filterFlower(List<Flower> flowers, String color, Integer price, Boolean flag) {
List<Flower> resList = new ArrayList<>();
for (Flower flower : flowers) {
if ((flag && flower.getPrice() < price) ||
(!flag && StringUtils.equals(color, flower.getColor()))) {
resList.add(flower);
}
}
return resList;
}
复制代码
通过flag
来控制要筛选价格类型的花还是颜色类型的花,但是这种写法实在是不美观。
那么,我们既然都能把花的属性作为参数进行传递,那么我们能不能我们能不能把过滤花的这种行为也作为一个参数进行传递,想着想着,你就动起了手:
首先定义一个过滤行为的接口:
interface FilterPrecidate {
boolean test(Flower flower);
}
复制代码
然后自定义两个行为过滤类继承这个接口:
class RedColorFilterPredicate implements FilterPrecidate {
@Override
public boolean test(Flower flower) {
return StringUtils.equals("red", flower.getColor());
}
}
class LowPriceFilterPredicate implements FilterPrecidate {
@Override
public boolean test(Flower flower) {
return flower.getPrice() < 8;
}
}
复制代码
然后重写我们的过滤方法,通过将行为作为参数传递:
public static List<Flower> filterFlower(List<Flower> flowers, FilterPrecidate filter) {
List<Flower> resList = new ArrayList<>();
for (Flower flower : flowers) {
if (filter.test(flower)) {
resList.add(flower);
}
}
return resList;
}
/***** 使用 *****/
filterFlower(flowerList,new RedColorFilterPredicate());
filterFlower(flowerList,new LowPriceFilterPredicate());
复制代码
这样子我们的代码已经很明了,但是我们再观察一下上面的方法,filterFlower()
这个方法只能传递对象作为参数,而FilterPrecidate
对象的核心方法也只有test()
,如果我们有新的行为就需要新建一个类继承FilterPrecidate
接口实现test()
方法。那么我们有没有办法直接将test()
这一个行为作为参数传递,答案是有的:Lombda.
filterFlower(flowerList, (Flower flower) -> flower.getPrice() > 8);
复制代码
我们甚至可以将多种行为作为作为一个参数传递:
filterFlower(flowerList, (Flower flower) -> flower.getPrice() > 8 && StringUtils.equals("red", flower.getColor()));
复制代码
可以看到,行为参数化是一个很有用的模式,它能够轻松地使用不断变化的需求,这种模式可以把一个行为封装起来,并通过传递和使用创建的行为将方法的行为参数化。
它可以替代匿名类
如果我们将一个鲜花的集合按照价格进行排序,我们会这样做:
Collections.sort(flowerList, new Comparator<Flower>() {
@Override
public int compare(Flower o1, Flower o2) {
return o1.getPrice().compareTo(o2.getPrice());
}
});
复制代码
那么通过行为参数化我们可以这样写:
Collections.sort(flowerList,(o1, o2) -> o1.getPrice().compareTo(o2.getPrice()));
复制代码
也可以这样写:
Collections.sort(flowerList, Comparator.comparing(Flower::getPrice));
复制代码
甚至可以这样写:
flowerList.sort(Comparator.comparing(Flower::getPrice));
复制代码
对比一下传统写法,你是不是已经开始爱上这种方式的写法了