Java 8 lambdas,Function.identity()或t-> t

问题

我对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(反之亦然)。我认为第二种选择更具可读性(当然是品味的问题)。但是,有没有"真正的"理由为什么应该首选?


#1 热门回答(150 赞)

从当前的JRE实现开始,Function.identity()将始终返回相同的实例,而每次出现的identifier -> identifier将不仅创建自己的实例,而且甚至具有不同的实现类。有关更多详细信息,请参阅here

原因是编译器生成一个合成方法,该方法保存该lambda表达式的普通主体(在x->x的情况下,相当于return identifier;)并告诉运行时创建调用此方法的函数接口的实现。因此,运行时只能看到不同的目标方法,并且当前实现不会分析方法以确定某些方法是否相同。

所以使用Function.identity()而不是x -> x可能会节省一些内存,但如果你真的认为x -> xFunction.identity()更具可读性,则不应该推动你的决定。

你还可以考虑在启用调试信息进行编译时,合成方法将具有指向包含lambda表达式的源代码行的行调试属性,因此你有可能在调试时查找特定的Function实例的源。相反,在调试操作时遇到Function.identity()返回的实例时,你将不知道谁调用了该方法并将实例传递给操作。


#2 热门回答(53 赞)

在你的例子中,str -> str和577418760之间没有太大的区别,因为在内部它只是t->t

但有时我们不能使用Function.identity因为我们不能使用aFunction。看看这里:

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();

你将收到自mapToIntexpectsToIntFunction以来的编译错误,这与Function无关。另外ToIntFunction没有identity()方法。


#3 热门回答(26 赞)

来自JDK source

static <T> Function<T, T> identity() {
    return t -> t;
}

所以,不,只要它在语法上是正确的。