本篇文章主要对《Java8实战》一书的前两章进行读书笔记的总结
文章目录
第一章:为什么要关心Java8
本章主要阐述了四个方面的内容:
- Java为什么还在变化
- 多核处理器的发展和大数据领域的出现对Java带来的影响
- 函数式编程的优势
- Java8的核心新特性
下面将针对上述的四个方面展开进行笔记
Java为什么还在变化
从Java出现到现在这二十几年的时间里,计算机领域的变化日新月异,Java如果要在竞争激烈的编程语言领域捍卫自己的地位,就必须通过改变来适应技术的发展和时代的要求。比如通过引入泛型解决了Java开发者对于集合类型的混淆;通过引入for-each的语法糖来降低了Iterator的暴露,从而提高了代码的可读性。同样,Java8的出现也是为了迎合计算机硬件的发展,应对其他基于语言对Java带来的挑战。文章中有一句话说的挺好:
使用Java8就是在保护作为Java程序员的职业生涯。
多核处理器的发展和大数据领域的出现对Java带来的影响
计算机硬件的发展赋予了计算机更强大的计算能力,大数据领域的出现又促使开发者需要充分利用这一强大的计算能力来进行,Java需要改变来适应应用环境的变化
函数式编程的优势
函数式编程的行为参数化能够让我们的代码更为简洁,可读性更强,灵活性也更高,除了下面列举的Java8核心新特性之外,函数式编程还带来了Optionnal类解决了空指针带来的极大危害;函数式编程中的模式匹配思想能够比if-then-else更简明得表达编程思想。
Java8的核心新特性
- 方法引用
- lamda表达式
- 行为参数化
- 流stream
- 默认方法
第二章:通过行为参数化传递代码
本章的内容总结起来为两个:
- 什么是行为参数化
- 通过一个例子让我们深刻体会到行为参数化的优势
什么是行为参数化
行为参数化就是可以帮助你处理频繁变更的需求的一种软件开发模式。通过行为参数化,利用其惰性执行的特点,能够帮助我们包装一段代码块而不执行它,然后把它放在任何一个需要这段代码块的地方,这个包装代码块的过程,就是行为参数化。
通过一个例子来感受行为参数化
现在有一个需求,对农场的苹果进行筛选,将绿色的苹果筛选出来:
public static List<Apple> filterGreenApples(List<Apple> inventory) {
List<Apple> result = new ArrayList();
for (Apple apple : inventory) {
if ("green".equals(apple.getColor())) {
result.add(apple);
}
}
return result;
}
第一次需求变更了:把另一种颜色的苹果筛选出来。
在这种情况下,我们会选择在上述的方法中添加一个颜色参数:
public static List<Apple> filterGreenApples(List<Apple> inventory, String color) {
List<Apple> result = new ArrayList();
for (Apple apple : inventory) {
if (color.equals(apple.getColor())) {
result.add(apple);
}
}
return result;
}
这样做就可以无所谓颜色了,当需要筛选任何一个颜色的时候,只需要告诉我们颜色就行了:
List<Apple> redApples = filterGreenApples(inventory, "red");
List<Apple> yellowApples = filterGreenApples(inventory, "yellow");
第二次需求变更:不仅要筛选颜色,还要筛选重量:
public static List<Apple> filterApples(List<Apple> inventory, String color, int weight, boolean flag) {
List<Apple> result = new ArrayList();
for (Apple apple : inventory) {
// 如果flag=true,说明要筛选颜色,否则说明要筛选重量
if ((flag && color.equals(apple.getColor())) || (!flag && apple.getWeight() <= weight)) {
result.add(apple);
}
}
return result;
}
这么写完,用起来的时候就比较笨拙了:
List<Apple> redApples = filterApples(inventory, "red", 0, true);
List<Apple> heavyApples = filterApples(inventory, "", 100, false);
可以看出来,这么写非常的笨拙,可读性很差,再来一个属性的时候就很尴尬了。所以我们接下来要把逻辑抽象一下,对比几次需求变更,变了什么,没变什么。我们可以看出来,变的是判断标准,没变的是判断的行为逻辑,因此我们要使用行为参数化了,将判断行为作为一个参数传到判断逻辑中。那么我们要怎么参数化我们的行为呢?先看看通用的一个办法。
通过接口实现
创建个接口:
public interface ApplePredicate {
boolean test(Apple apple);
}
然后让前面的两个判断标准分别实现这个接口,从而将二者与判断逻辑解耦:
// 绿苹果筛选
class GreenApplePredicate implements ApplePredicate {
@Override
public boolean test(Apple apple) {
if ("green".equals(apple.getColor())) {
return true;
}
return false;
}
}
// 重苹果筛选
class HeavyApplePredicate implements ApplePredicate {
@Override
public boolean test(Apple apple) {
if (apple.getWeight() > 100) {
return true;
}
return false;
}
}
最后,把原来的判断逻辑改一下:
public static List<Apple> filterApples(List<Apple> inventory, ApplePredicate applePredicate) {
List<Apple> result = new ArrayList();
for (Apple apple : inventory) {
if (applePredicate.test(apple)) {
result.add(apple);
}
}
return result;
}
这样用起来也会很方便,代码看着也是神清气爽:
List<Apple> greenApples = filterApples(inventory, new GreenApplePredicate());
List<Apple> heavyApples = filterApples(inventory, new HeavyApplePredicate());
从上面的代码中,虽然我们通过抽象,把之前乱成一团的逻辑解耦,将代码拆解的清晰,可扩展。但是,我们发现代码非常多,每次加一个判断条件,我们都要创建一个实现这个接口的类,那有没有可以更方便的方法?jdk8之前,留给我们的选择就只有匿名类了:
通过匿名内部类实现
List<Apple> heavyApples = filterApples(inventory, new ApplePredicate() {
@Override
public boolean test(Apple apple) {
if (apple.getWeight() > 100) {
return true;
}
return false;
}
});
虽然省去了创建一个新的类,但是代码量并没有减少。那么来到jdk8,我们有了新的办法,可以用lambda表达式来替换上面的匿名内部类
通过lambda实现
List<Apple> heavyApples = filterApples(inventory, (Apple apple) -> apple.getWeight() > 100);
可以看到,lambda表达式让我们的代码瞬间简介了很多,并且可读性与匿名内部类相比也提高了不少。文中有一个图非常恰当地解释了行为参数化的集中实现方式与值参数化在代码灵活性和简洁性方面的比较:
我们也可以把我们的这段筛选逻辑应用在其他例子上,只需要对接口类型也抽象一下:
public interface Predicate<T> {
boolean test(T t);
}
public static <T> List<T> filter(List<T> inventory, Predicate<T> p) {
List<T> result = new ArrayList<>();
for (T e : inventory) {
if (p.test(e)) {
result.add(e);
}
}
return result;
}
我们可以使用上面的接口来过滤任何我们想要过滤的列表了,比如:
List<Integer> smallerThanTenNumbers = filter(inventory, i -> i < 10);
List<String> stringsContainsm = filter(inventory, e -> e.contains("m"));
通过上面的例子,我们已经能够感受到行为参数化的魅力了。
更多实例
在java中使用行为参数化的例子比比皆是,我们实际应用中使用比较广泛的三个例子:
- Comparator接口实现自定义比较器
我们来自定义一个比较器,实现苹果重量的排序:
inventory.sort((a1,a2)->a1.getWeight().compareTo(a2.getWeight()));
- Runnable或Callable接口实现多线程操作
new Thread(()-> System.out.println("hello world")).start();
- GUI事件处理
Button button=new Button("Hello");
button.addActionListener(e -> System.out.println(e.getActionCommand()));