Java 8是对Java语言最重要的更新,自启动以来,包含了大量新功能,您可能会想知道从哪里开始。在这个系列中,作者和教育家Venkat Subramaniam提供了Java 8的一种惯用方法:简短的探索,邀请您重新思考您认为理所当然的Java惯例,同时逐渐将新技术和语法集成到您的程序中。
在Java 8发布大约一年之后,我在我的网站上发布了一个简短而不受控制的调查,邀请开发者参与。每个参与者都在命令式或功能式中呈现一段代码,然后要求确定代码的行为。我测量了每个访问者产生响应的时间,比较了两个不同代码示例的结果。这次调查是开放了48小时,当时有超过1100人参加。结果包含了一些惊喜。
大多数开发人员,都有很多丰富的编程风格经验。尽管功能风格已经存在了很长时间,但是对于大多数Java程序员来说却并不熟悉。了解到这一点,82%的接受命令式编码的调查对象能够确定其正确的行为并不奇怪。与此同时,只有百分之七十五的获得功能风格代码的受访者得到了正确的答案。
然而,令我感到意外的是受访者了解这两个代码样本的时间。计算命令式代码的中位时间比功能式代码的中位时间长30秒。
如果写得很好,功能风格的代码比命令风格的代码更具表现力和简洁性。
我们来看下面这2个例子
命令性代码示例
List<String> names = Arrays.asList("Jack", "Jill", "Nate", "Kara", "Kim", "Jullie", "Paul", "Peter");
List<String> subList = new ArrayList<>();
for(String name : names) {
if(name.length() == 4)
subList.add(name);
}
StringBuilder namesOfLength4 = new StringBuilder();
for(int i = 0; i < subList.size() - 1; i++) {
namesOfLength4.append(subList.get(i));
namesOfLength4.append(", ");
}
if(subList.size() > 1)
namesOfLength4.append(subList.get(subList.size() - 1));
System.out.println(namesOfLength4);
你需要多少时间来弄清这个代码?如果花费的时间比您预期的要长,请不要感到惊讶。这不代表你的能力,代码质量差。
现在用Java 8支持的功能风格编写等效示例:
List<String> names = Arrays.asList("Jack", "Jill", "Nate", "Kara", "Kim", "Jullie", "Paul", "Peter");
System.out.println(
names.stream()
.filter(name -> name.length() == 4)
.collect(Collectors.joining(", ")));
需要多少时间来弄清楚这个代码?显然,你已经确定了目的,所以这不是一个真正的实验。如果你想真的比较样本,请问几个同事找出一个代码样本或其他,然后比较他们的响应时间。
如果您熟悉Java 8,那么您可能无需弄清楚第2个例子中的代码。即使您对Java 8不熟悉,也可以使用描述性方法名称。您还可以快速理解代码,因为它比例子1简洁得多。
从本质上讲,代码说:给定一个名称集合,只选择长度为4的名称,然后用逗号连接它们。
这个例子是人为设计的,但它确实说明了编码的简洁性和表达性的价值。我们在函数式代码中比在命令式代码中看到更多这些特性。
功能风格的代码是表达性和简洁性的,这导致程序既短又易于阅读。再看一个例子:
命令性代码示例
int result = 0;
for(int e : numbers) {
if(e > 3 && e % 2 == 0 && e < 8) {
result += e * 2;
}
}
System.out.println(result);
给定列表调用numbers
,这个代码执行一个大于3小于8的偶数的和,然后打印结果。代码由七条线组成,我们可以减少一两条线。
现在用Java 8支持的功能风格编写等效示例:
System.out.println(
numbers.stream()
.filter(e -> e > 3)
.filter(e -> e % 2 == 0)
.filter(e -> e < 8)
.mapToInt(e -> e * 2)
.sum());
这也是七行代码,但在这种情况下,进一步减少代码是没有帮助的。
功能风格的代码并不总是比命令风格的代码短。更重要的是它具有表现力。代码简洁但难于阅读是没有帮助的。
功能风格的代码被设计成比命令式代码更简洁,但是不能确保它更具可读性。看看下面这个例子就知道了。
System.out.println(
names.stream().
filter(name -> name.startsWith("J")).
filter(name -> name.length() > 3)
.map(name -> name.toUpperCase()).collect(Collectors.joining(", ")));
这个例子中,filter
,map
和其它功能元件增加了代码的表现力。但是你可能会注意到这个代码更简洁。
虽然只有两行,但这些代码仍然需要相当大的努力来阅读和理解。你的眼睛紧张,看看一个函数调用结束,下一个开始。代码非常简短,但是写得很简单。编写这种无用的代码只有一个理由:开发人员必须憎恨与他们一起工作的每个人。
在编程中,我们很容易忽视表现力和可读性的价值。Java 8通过惯例鼓励这些特性,这表明我们为了函数组合而垂直对齐点。
不幸的是,即使在多次提醒之后,程序员经常忽略这个约定。更有经验的程序员应该在代码审查期间强制执行这个约定。
下面这个例子使用对齐约定重写上面例子的代码时会发生什么情况:
Java中的函数组合:
System.out.println(
names.stream()
.filter(name -> name.startsWith("J"))
.filter(name -> name.length() > 3)
.map(name -> name.toUpperCase())
.collect(Collectors.joining(", ")));
这里我们看到例5中的简洁代码,但这些点是垂直对齐的,而且我们拒绝将多个条件组合成一个参数的冲动。因此,每条路线都是凝聚力:狭隘和专注,只有一个明确的任务。
虽然这看起来可能是可有可无的,但遵循Java 8的对齐约定是非常有益的。
遵循这个约定的代码更容易阅读,理解和解释。在详细考察每个部分之前,我们可以迅速掌握总体目标。
元素清晰,易于定位,支持更快的修改。如果我们想要包含另一个条件,或者删除或修改一个现有条件,找到这条线并进行更改将会相对简单。
代码更容易维护,这表示我们关心我们团队中的其他开发人员。编写有用的代码可以极大地影响团队的士气,除了使代码更容易维护。
保持代码的每一行简明扼要是一个很好的做法,但是过度会导致代码变得简单而难以阅读。为了提高表达能力,问问自己代码是否容易理解。为了提高可读性,应用Java 8的垂直排列点约定。使用这些简单的技巧,您将创建简洁,富有表现力和可读性的功能风格的代码。
原文:https://www.ibm.com/developerworks/java/library/j-java8idioms4/index.html
翻译和整理:刘志鹏
推荐阅读
推荐阅读
JAVA葵花宝典
长按关注置顶
java知识和技术查漏补缺,空余时间学习碎片化知识,分享开发、运维、架构等综合性知识,助力职场最后一公里与职业进阶,每天看宝典,就选它。