我有一个关于Function.identity()方法使用的问题。
想象下面的代码:
Arrays.asList("a", "b", "c")
.stream()
.map(Function.identity()) // <- This,
.map(str -> str) // <- is the same as this.
.collect(Collectors.toMap(
Function.identity(), // <-- And this,
str -> str)); // <-- is the same as this.
是否有任何理由你应该使用Function.identity()而不是str->str(反之亦然)。我认为第二种选择更具有可读性(当然这是一种品味问题)。但是,有什么“真正”的理由让一个人更受青睐吗?
在当前的JRE实现中,Function.identity()总是返回相同的实例,而每次出现标识符->标识符不仅创建自己的实例,而且甚至具有不同的实现类。更多细节,请看这里。
原因是编译器生成一个合成方法,保存该lambda表达式的普通主体(在x->x的情况下,相当于返回标识符;),并告诉运行时创建调用该方法的函数接口的实现。因此,运行时只能看到不同的目标方法,而当前实现不会分析这些方法,以确定某些方法是否等效。
因此,使用Function.identity()而不是x -> x可能会节省一些内存,但如果你真的认为x -> x比Function.identity()更可读,那就不应该影响你的决定。
您还可以考虑在启用调试信息的情况下进行编译时,合成方法将具有一个行调试属性,指向持有lambda表达式的源代码行,因此在调试时您有机会找到特定Function实例的源。相反,当在调试操作期间遇到Function.identity()返回的实例时,您将不知道是谁调用了该方法并将实例传递给了操作。
在你的例子中,str -> str和Function.identity()之间没有太大的区别,因为在内部它只是t->t。
但有时我们不能使用函数。因为我们不能使用函数。看看这里:
List<Integer> list = new ArrayList<>();
list.add(1);
list.add(2);
这样编译就可以了
int[] arrayOK = list.stream().mapToInt(i -> i).toArray();
但是如果你尝试编译
int[] arrayProblem = list.stream().mapToInt(Function.identity()).toArray();
你会得到编译错误,因为mapToInt期望ToIntFunction,这与Function无关。另外ToIntFunction没有identity()方法。
在当前的JRE实现中,Function.identity()总是返回相同的实例,而每次出现标识符->标识符不仅创建自己的实例,而且甚至具有不同的实现类。更多细节,请看这里。
原因是编译器生成一个合成方法,保存该lambda表达式的普通主体(在x->x的情况下,相当于返回标识符;),并告诉运行时创建调用该方法的函数接口的实现。因此,运行时只能看到不同的目标方法,而当前实现不会分析这些方法,以确定某些方法是否等效。
因此,使用Function.identity()而不是x -> x可能会节省一些内存,但如果你真的认为x -> x比Function.identity()更可读,那就不应该影响你的决定。
您还可以考虑在启用调试信息的情况下进行编译时,合成方法将具有一个行调试属性,指向持有lambda表达式的源代码行,因此在调试时您有机会找到特定Function实例的源。相反,当在调试操作期间遇到Function.identity()返回的实例时,您将不知道是谁调用了该方法并将实例传递给了操作。