使用 Java8 修复代码烂味道

在学习 Java8 时发现一些写法真的很优秀,用了就有种回不去的感觉,很好的处理了 Java 代码上的烂味道。

匿名内部类

在任何使用内部类的地方都使用 lamdba表达式 进行重构,比如:

1
2
3
4
5
list.sort(new Comparator<String>() {
public int compare (String o1, String o2) {
return o1.length() - o2.length();
}
});

可以更简洁的写成:

1
list.sort((o1,o2) -> o1.length() - o2.length());

静态类

通常我们用 Util 或者 Helper 命名结尾的类来包含静态方法,现在我们可以使用 interface 来支持静态方法,类可能比接口更好用,但是如果方法是无状态且设计出来为了重写了,可能方法存在接口中更好。

嵌套的 for/if 语句

Stream API 设计出来就是为了更高效的查询 Collections,当你看到下面的代码时:

1
2
3
4
5
6
7
List<Field> validFields = new ArrayList<Field>();
for (Field field : fields) {
if (meetsCriteria(field)) {
validFields.add(field);
}
}
return validFields;

你应该考虑使用 Stream API代替,这种情况,使用 filter 和 collect 代替更合适:

1
2
3
return fields.stream()
.filter(this::meetsCriteria)
.collect(Collectors.toList());

对于循环内部的 if 语句可以使用 anyMatch 或者 findFirst 进行重构:

1
2
3
4
5
6
for (String current : strings) {
if (current.equals(wanted)) {
return true;
}
}
return false;

可以重构为:

1
2
return strings.stream()
.anyMatch(current -> current.equals(wanted));

和:

1
2
3
4
5
6
for (String current : strings) {
if (current.equals(wanted)) {
return current;
}
}
return null;

可以重构为:

1
2
3
4
return strings.stream()
.filter(current -> current.equals(wanted))
.findFirst()
.orElse(null);

这里的 orElse null 看起来非常难看,我们会在后面进行重构。

一个 Collection 多个操作

为了高效执行代码,我们通常会在一个 Collection 或者多个 Collection 中执行多个操作来取得结果,考虑下面的代码:

1
2
3
4
5
6
7
8
9
10
11
 // 收集登录消息
List<LogLine> lines = new ArrayList<>();
for (Message message : messages) {
lines.add(new LogLine(message));
}
// 排序
Collections.sort(lines);
// 记录他们
for (LogLine line : lines) {
line.log(LOG);
}

分离的步骤可以让我们看清每一步的操作,但在调用 Collection.sort 时我们就该考虑到使用 Streams API,如果我们这样做,就可以将这些多个操作合并为一个 stream :

1
2
3
4
messages.stream()
.map(LogLine::new)
.sorted()
.forEach(logLine -> logLine.log(LOG));

这样做不仅削减了一个 Collection,而且可读性更高,代码执行效率也高。

使用 Iterator remove 元素

在 java8 之前的代码可能像这样:

1
2
3
4
5
6
7
Iterator<String> iterator = strings.iterator();
while (iterator.hasNext()) {
String current = iterator.next();
if (current.endsWith(“jnilib”)) {
iterator.remove();
}
}

现在,代码可以这样写:

1
strings.removeIf (current -> current.endsWith(“jnilib”));

同样的代码不仅看起来可读性高,而且执行效率更高。

Null 检查

NullPointerException 严重影响着 Java 开发人员的工作,为了避免遇到空指针异常我们都会对可能出现 Null 的地方进行检查,采用 Optional 意味着我们可以更明确的知道一个方法期望的返回类型和消除不必要的 Null 检查,考虑我们前面的 orElse(null) :

1
2
3
4
5
6
7
public static String findString (String wanted) {
List<String> strings = new ArrayList<>();
return strings.stream()
.filter(current ->current.equals(wanted))
.findFirst()
.orElse(null);
}

任何调用 findString 的代码都必须检查值是否为空,如果是的话需要采取适当的行动。

1
2
3
4
5
6
7
String foundString = findString(wantedString);
if (foundString == null) {
return “Did not find value” and
wantedString;
} else {
return foundString;
}

这看起来有些丑以及繁琐,如果我们使用 Optional 代替:

1
2
3
4
5
6
public static Optional<String> findString(String wanted) {
List<String> strings = new ArrayList<>();
return strings.stream()
.filter(current ->current.equals(wanted))
.findFirst();
}

这样我们可以更优雅的处理值找不到的情况:

1
2
return findString(wantedString)
.orElse(“Did notfind value” and wantedString);