使用Java 8流时,我经常发现我需要重构一个多语句lambda表达式。我将用一个简单的例子来说明这一点。假设我已经开始编写这段代码:
Stream.of(1, 3).map(i -> {
if (i == 1) {
return "I";
} else if (i == 3) {
return "E";
}
return "";
}).forEach(System.out::println);
现在我不太喜欢map
电话中的大型lambda表达式。因此,我想把它重构出来。我看到两个选项,要么在我的班级中创建Function
的实例:
private static Function<Integer, String> mapper = i -> {
if (i == 1) {
return "I";
} else if (i == 3) {
return "E";
}
return "";
};
并像这样使用它:
Stream.of(1, 3).map(mapper).forEach(System.out::println);
或者我只是制作方法:
private static String map(Integer i) {
if (i == 1) {
return "I";
} else if (i == 3) {
return "E";
}
return "";
}
并使用方法参考:
Stream.of(1, 3).map(Test::map).forEach(System.out::println);
除了明显的味道问题外,这两种方法都有任何优点或缺点吗?
例如,我知道堆栈跟踪在方法参考案例中变得更具可读性,这是一个小优势。
答案 0 :(得分:5)
除非有一些我不知道的额外魔法,否则当前的lambda实现会将你的非捕获lambda变为静态方法,并将缓存lambda实例。通过明确地执行相同的操作(static final
对lambda的引用),您基本上复制了隐式工作,因此您最终会对同一事物进行两次缓存引用。你也在打败lambda实例的延迟初始化,否则你将获得免费的。
这就是我更喜欢方法参考的原因:它更易于编写,更具惯用性,而且在实现方面似乎也更轻量级。
答案 1 :(得分:5)
将多行lambda表达式重构为普通方法并在流中使用方法引用的一些原因是可维护性和可测试性。
当将非普通映射器函数转换为普通方法时,它会获取一个名称,并且它可用于单元测试框架。您可以轻松编写直接调用它的测试,并在必要时将其删除。
该方法还可以包含与之关联的文档注释,包括参数和返回值的文档。
如果mapper函数很复杂,这两个都非常有用。
将lambda表达式赋给字段并没有错,但我认为这样做的主要原因是如果在运行时修改字段,例如,当应用程序的状态发生变化时。即使在这些情况下,我也可以考虑编写普通方法,然后使用方法引用而不是多行lambda表达式来分配字段。
使用字段的一个缺点是它要求您明确声明功能接口的泛型类型参数。 IDE可以为此提供帮助,但它会增加程序的混乱。
我猜想使用方法引用的普通方法总是优于使用用lambda表达式初始化的final字段。