您的位置:首页 > 编程语言 > Java开发

学习Java 8的编码风格带来什么样的好处

2017-12-14 00:00 92 查看
表现力是功能风格编程的好处之一,但是这对你的代码意味着什么?在本文中,我们比较命令式和功能式代码的例子,识别表达性和简洁性。你会看到这些特质是如何支持可读性的,你会考虑一个相反的例子:当简洁的要求导致无用的代码。最后,我们考察Java 8在函数合成中垂直对齐点的习惯。虽然这个约定对某些Java开发人员可能并不熟悉,但是一个简单的例子说明了它的价值。

关于java8Java 8是对Java语言最重要的更新,自启动以来,包含了大量新功能,您可能会想知道从哪里开始。在这个系列中,作者和教育家Venkat Subramaniam提供了Java 8的一种惯用方法:简短的探索,邀请您重新思考您认为理所当然的Java惯例,同时逐渐将新技术和语法集成到您的程序中。


令人惊讶的结果

在Java 8发布大约一年之后,我在我的网站上发布了一个简短而不受控制的调查,邀请开发者参与。每个参与者都在命令式或功能式中呈现一段代码,然后要求确定代码的行为。我测量了每个访问者产生响应的时间,比较了两个不同代码示例的结果。这次调查是开放了48小时,当时有超过1100人参加。结果包含了一些惊喜。

大多数开发人员,都有很多丰富的编程风格经验。尽管功能风格已经存在了很长时间,但是对于大多数Java程序员来说却并不熟悉。了解到这一点,82%的接受命令式编码的调查对象能够确定其正确的行为并不奇怪。与此同时,只有百分之七十五的获得功能风格代码的受访者得到了正确的答案。然而,令我感到意外的是受访者了解这两个代码样本的时间。计算命令式代码的中位时间比功能式代码的中位时间长30秒。在家尝试这个实验如果写得很好,功能风格的代码比命令风格的代码更具表现力和简洁性。我们来看下面这2个例子[b]命令性代码示例[/b]
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

翻译和整理:刘志鹏

推荐阅读
win10系统同时装win7系统详细图解
提供一个免费学习和交流的场所
Java程序员最常用的Linux命令


JAVA葵花宝典

长按关注置顶java知识和技术查漏补缺,空余时间学习碎片化知识,分享开发、运维、架构等综合性知识,助力职场最后一公里与职业进阶,每天看宝典,就选它。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: