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

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

我对Function.identity()方法的用法有疑问。

想象一下以下代码:

1
2
3
4
5
6
7
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()将始终返回相同的实例,而每次出现的identifier -> identifier不仅会创建自己的实例,甚至还会有一个不同的实现类。有关详细信息,请参阅此处。

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

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

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


在您的示例中,str -> strFunction.identity()之间没有太大区别,因为内部只是t->t

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

1
2
3
List<Integer> list = new ArrayList<>();
list.add(1);
list.add(2);

这将编译好

1
int[] arrayOK = list.stream().mapToInt(i -> i).toArray();

但如果你试图编译

1
int[] arrayProblem = list.stream().mapToInt(Function.identity()).toArray();

你会得到编译错误,因为mapToInt需要ToIntFunction,这与Function无关。 ToIntFunction也没有identity()方法。


来自JDK来源:

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

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