Java8-19-lambda 重构代码
内容导读
互联网集市收集整理的这篇技术教程文章主要介绍了Java8-19-lambda 重构代码,小编现在分享给大家,供广大互联网技能从业者学习和参考。文章包含5075字,纯文字阅读大概需要8分钟。
内容图文
通过本书的前七章,我们了解了Lambda和Stream API的强大威力。
你可能主要在新项目的代码中使用这些特性。如果你创建的是全新的Java项目,这是极好的时机,你可以轻装上阵,迅速地将新特性应用到项目中。然而不幸的是,大多数情况下你没有机会从头开始一个全新的项目。很多时候,你不得不面对的是用老版Java接口编写的遗留代码。
这些就是本章要讨论的内容。我们会介绍几种方法,帮助你重构代码,以适配使用Lambda表达式,让你维护的代码具备更好的可读性和灵活性。
除此之外,我们还会讨论目前比较流行的几种面向对象的设计模式,包括策略模式、模板方法模式、观察者模式、责任链模式,以及工厂模式,在结合Lambda表达式之后变得更简洁的情况。
最后,我们会介绍如何测试和调试使用Lambda表达式和Stream API的代码。
为改善可读性和灵活性重构代码
从本书的开篇我们就一直在强调,利用Lambda表达式,你可以写出更简洁、更灵活的代码。
用“更简洁”来描述Lambda表达式是因为相较于匿名类,Lambda表达式可以帮助我们用更紧凑的方式描述程序的行为。
第3章中我们也提到,如果你希望将一个既有的方法作为参数传递给另一个方法,那么方法引用无疑是我们推荐的方法,利用这种方式我们能写出非常简洁的代码。
采用Lambda表达式之后,你的代码会变得更加灵活,因为Lambda表达式鼓励大家使用第2章中介绍过的行为参数化的方式。
在这种方式下,应对需求的变化时,你的代码可以依据传入的参数动态选择和执行相应的行为。
这一节,我们会将所有这些综合在一起,通过例子展示如何运用前几章介绍的Lambda表达式、方法引用以及Stream接口等特性重构遗留代码,改善程序的可读性和灵活性。
改善代码的可读性
改善代码的可读性到底意味着什么?
我们很难定义什么是好的可读性,因为这可能非常主观。通常的理解是,“别人理解这段代码的难易程度”。改善可读性意味着你要确保你的代码能非常容易地被包括自己在内的所有人理解和维护。为了确保你的代码能被其他人理解,有几个步骤可以尝试,比如确保你的代码附有良好的文档,并严格遵守编程规范。
跟之前的版本相比较,Java 8的新特性也可以帮助提升代码的可读性:
-
使用Java 8,你可以减少冗长的代码,让代码更易于理解
-
通过方法引用和Stream API,你的代码会变得更直观
这里我们会介绍三种简单的重构,利用Lambda表达式、方法引用以及Stream改善程序代码的可读性:
-
重构代码,用Lambda表达式取代匿名类
-
用方法引用重构Lambda表达式
-
用Stream API重构命令式的数据处理
从匿名类到 Lambda 表达式的转换
你值得尝试的第一种重构,也是简单的方式,是将实现单一抽象方法的匿名类转为Lambda表达式。
为什么呢?
前面几章的介绍应该足以说服你,因为匿名类是极其繁琐且容易出错的。
采用Lambda表达式之后,你的代码会更简洁,可读性更好。
还记得第3章的例子就是一个创建Runnable 对象的匿名类,这段代码及其对应的Lambda表达式实现如下:
Runnable r1 = new Runnable(){
public void run() {
System.out.println("Hello");
}
};
Runnable r2 = () -> System.out.println("Hello");
转换需要注意的地方
但是某些情况下,将匿名类转换为Lambda表达式可能是一个比较复杂的过程。
-
首先,匿名类和Lambda表达式中的 this 和 super 的含义是不同的。在匿名类中, this 代表的是类自身,但是在Lambda中,它代表的是包含类。
-
其次,匿名类可以屏蔽包含类的变量,而Lambda表达式不能(它们会导致编译错误),譬如下面这段代码:
int a = 10;
Runnable r1 = () -> {
// 编译错误
int a = 2;
System.out.println(a);
};
Runnable r2 = new Runnable(){
public void run(){
// 一切正常
int a = 2;
System.out.println(a);
}
};
最后,在涉及重载的上下文里,将匿名类转换为Lambda表达式可能导致最终的代码更加晦涩。
实际上,匿名类的类型是在初始化时确定的,而Lambda的类型取决于它的上下文。
通过下面这个例子,我们可以了解问题是如何发生的。
我们假设你用与 Runnable 同样的签名声明了一个函数接口,我们称之为 Task (你希望采用与你的业务模型更贴切的接口名时,就可能做这样的变更):
interface Task {
public void execute();
}
public static void doSomething(Runnable r) { r.run(); }
public static void doSomething(Task a) { a.execute(); }
现在,你再传递一个匿名类实现的 Task ,不会碰到任何问题:
doSomething(new Task() {
public void execute() {
System.out.println("Danger danger!!");
}
});
但是将这种匿名类转换为Lambda表达式时,就导致了一种晦涩的方法调用,因为 Runnable和 Task 都是合法的目标类型:
// 麻烦来了: doSomething(Runnable) 和doSomething(Task)都匹配该类型
doSomething(() -> System.out.println("Danger danger!!"));
你可以对 Task 尝试使用显式的类型转换来解决这种模棱两可的情况:
doSomething((Task)() -> System.out.println("Danger danger!!"));
但是不要因此而放弃对Lambda的尝试。
从 Lambda 表达式到方法引用的转换
Lambda表达式非常适用于需要传递代码片段的场景。
尽量使用方法引用
不过,为了改善代码的可读性,也请尽量使用方法引用。因为方法名往往能更直观地表达代码的意图。
比如,第6章中我们曾经展示过下面这段代码,它的功能是按照食物的热量级别对菜肴进行分类:
Map<Dish.CaloricLevel, List<Dish>> dishesByCaloricLevel = menu.stream().collect(
groupingBy(dish -> {
if (dish.getCalories() <= 400) {
return Dish.CaloricLevel.DIET;
} else if (dish.getCalories() <= 700) {
return Dish.CaloricLevel.NORMAL;
} else {
return Dish.CaloricLevel.FAT;
}
}));
你可以将Lambda表达式的内容抽取到一个单独的方法中,将其作为参数传递给 groupingBy 方法。
变换之后,代码变得更加简洁,程序的意图也更加清晰了:
Map<CaloricLevel, List<Dish>> dishesByCaloricLevel = menu.stream().collect(groupingBy(Dish::getCaloricLevel));
为了实现这个方案,你还需要在 Dish 类中添加 getCaloricLevel 方法:
public class Dish{
public CaloricLevel getCaloricLevel(){
if (this.getCalories() <= 400) {
return CaloricLevel.DIET;
} else if (this.getCalories() <= 700) {
return CaloricLevel.NORMAL;
} else {
内容总结
以上是互联网集市为您收集整理的Java8-19-lambda 重构代码全部内容,希望文章能够帮你解决Java8-19-lambda 重构代码所遇到的程序开发问题。 如果觉得互联网集市技术教程内容还不错,欢迎将互联网集市网站推荐给程序员好友。
内容备注
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 gblab@vip.qq.com 举报,一经查实,本站将立刻删除。
内容手机端
扫描二维码推送至手机访问。