简洁Java之道
作者:网络转载 发布时间:[ 2015/7/22 11:14:30 ] 推荐标签:测试开发技术
下方的示例代码解析了一个包含一个句子的字符串对象,并统计单词的数量和感兴趣的字母。包括空白行在内,整个代码清单的行数不超过70行。
1. import java.util.*;
2.
3. import static java.util.Arrays.asList;
4. import static java.util.function.Function.identity;
5. import static java.util.stream.Collectors.*;
6.
7. public class Main {
8.
9. public static void p(String s) {
10. System.out.println(s.replaceAll("[\]\[]", ""));
11. }
12.
13. private static List uniq(List letters) {
14. return new ArrayList(new HashSet(letters));
15. }
16.
17. private static List sort(List letters) {
18. return letters.stream().sorted().collect(toList());
19. }
20.
21. private static Map uniqueCount(List letters) {
22. return letters.stream().
23. collect(groupingBy(identity(), counting()));
24. }
25.
26. private static String getWordsLongerThan(int length, List words) {
27. return String.join(" | ", words
28. .stream().filter(w -> w.length() > length)
29. .collect(toList())
30. );
31. }
32.
33. private static String getWordLengthsLongerThan(int length, List words)
34. {
35. return String.join(" | ", words
36. .stream().filter(w -> w.length() > length)
37. .mapToInt(String::length)
38. .mapToObj(n -> String.format("%" + n + "s", n))
39. .collect(toList()));
40. }
41.
42. public static void main(String[] args) {
43.
44. String s = "The quick brown fox jumped over the lazy dog.";
45. String sentence = s.toLowerCase().replaceAll("[^a-z ]", "");
46.
47. List words = asList(sentence.split(" "));
48. List letters = asList(sentence.split(""));
49.
50. p("Sentence : " + sentence);
51. p("Words : " + words.size());
52. p("Letters : " + letters.size());
53.
54. p(" Letters : " + letters);
55. p("Sorted : " + sort(letters));
56. p("Unique : " + uniq(letters));
57.
58. Map m = uniqueCount(letters);
59. p(" Counts");
60.
61. p("letters");
62. p(m.keySet().toString().replace(",", ""));
63. p(m.values().toString().replace(",", ""));
64.
65. p(" words");
66. p(getWordsLongerThan(3, words));
67. p(getWordLengthsLongerThan(3, words));
68. }
69. }
示例程序执行输出:
Sentence : the quick brown fox jumped over the lazy dog
Words : 9
Letters : 44
Letters : t, h, e, , q, u, i, c, k, , b, r, o, w, n, , f, o, x, , j, u, m, p, e, d, , o, v, e, r, , t, h, e, , l, a, z, y, , d, o, g
Sorted : , , , , , , , , a, b, c, d, d, e, e, e, e, f, g, h, h, i, j, k, l, m, n, o, o, o, o, p, q, r, r, t, t, u, u, v, w, x, y, z
Unique : , a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, t, u, v, w, x, y, z
Counts
letters
a b c d e f g h i j k l m n o p q r t u v w x y z
8 1 1 1 2 4 1 1 2 1 1 1 1 1 1 4 1 1 2 2 2 1 1 1 1 1
words
quick | brown | jumped | over | lazy
5 | 5 | 6 | 4 | 4
上述代码已经经过了多重精简。其中一些方式并非在各个版本的Java中都可行,而且有些方式可能并不符合公认的编码风格指南。思考一下在较早版本的Java中如何才能够获得相同的输出?首先,需要创建许多局部变量用于临时存储数据或作为索引。其次,需要通过许多条件语句和循环告知Java如何处理数据。新的函数式编程方式更加专注于需要什么数据,而并不关心与其相关的临时变量、嵌套循环、索引管理或条件语句的处理。
在某些情况下,采用早期版本中的标准Java语法以减少代码量是以牺牲清晰度为代价的。例如,示例代码第一行的标准import语句中的Java包引用了java.util下的所有类,而不是根据类名分别引用。对System.out.println的调用被替换为对一个名为p的方法的调用,这样在每次方法调用时都可以使用短名称(行9-11)。由于可能违反某些Java的编码规范,这些改变富有争议,不过有着其他背景的程序开发人员查看这些代码时可能并不会有何问题。
另外一些情况下,则利用了从JDK8预览版才新增的功能特性。静态引用(行3-5)可以减少内联所需引用的类的数量。而正则表达式(行10,45)则可以用与函数式编程本身无关的方式,有效隐藏循环和条件语句。这些习语,特别是正则表达式的使用,经常会因为难以阅读和说明而受到质疑。如果运用得当,这些习语可以减少噪音的数量,并且能够限制开发人员需要阅读和说明的代码数量。
后,示例代码利用了JDK 8中新增的Streaming API。使用了Streaming API中大量的方法对列表进行过滤、分组和处理(行17-40)。尽管在IDE中它们与内附类的关联关系很清晰,不过除非你已经很熟悉这些API,否则这种关系并不是那么显而易见。下表展示了示例代码中所出现的每一次方法调用的来源。

uniq()(行13)和sort()(行17)方法体现了同名的Unix实用工具的功能。sort引入了对流的第一次调用,首先对流进行排序,然后再将排序后的结果收集到列表中。UniqueCount()(行21)与uniq -c类似,返回一个map对象,其中每个键是一个字符,每个值则是这个字符出现次数的统计。两个“getWords”方法(行26和行33)用于过滤出比给定长度短的单词。getWordLengthsLongerThan()方法调用了一些额外的方法,将结果格式化并转换成不可修改的String对象。
整段代码并未引入任何与Lambda表达式相关的新概念。之前所介绍的语法只适用于Java Stream API特定的使用场景。
总结
用更少的代码实现同样任务的理念与爱因斯坦的理念一致:“必须尽可能地简洁明了,但又不能简单地被简单。”Lambda表达式和新的Stream API因其能够实现扩展性良好的简洁代码而备受关注。它们让程序开发人员可以恰当地将代码简化成好的表现形式。
函数式编程习语的设计理念是简短,而且仔细思考一下会发现许多可以让Java代码更加精简的场景。新的语法虽然有点陌生但并非十分复杂。这些新的功能特性清晰地表明,作为一种语言,Java已经远远超越其初的目标。它正在用开放的态度接受其他程序设计语言中出色的一些功能,并将它们整合到Java之中。

sales@spasvo.com