在 Java 中,为什么 Function.identity() 是静态方法而不是其他方法?

Mar*_*ark 0 java functional-programming java-8 functional-interface

Java 8 添加了函数式编程结构,包括Function类及其关联identity()方法。

这是此方法的当前结构:

// Current implementation of this function in the [JDK source][1]
static <T> Function<T, T> identity() {
    return t -> t;
}

// Can be used like this
List<T> sameList = list.stream().map(Function.identity()).collect(Collectors.toList());
Run Code Online (Sandbox Code Playgroud)

但是,还有第二种构造它的方法:

// Alternative implementation of the method
static <T> T identity(T in) {
    return in;
}

// Can be used like this
List<T> sameList = list.stream().map(Function::identity).collect(Collectors.toList());
Run Code Online (Sandbox Code Playgroud)

甚至还有第三种方式来构建它:

// Third implementation
static final Function<T, T> IDENTITY_FUNCTION = t -> t;

// Can be used like this
List<T> sameList = list.stream().map(Function.IDENTITY_FUNCTION).collect(Collectors.toList());
Run Code Online (Sandbox Code Playgroud)

在这三种方法中,JDK 中实际使用的第一种方法看起来内存效率较低,因为它似乎在每次使用时都创建一个新对象 (lambda),而第二个和第三个实现则没有。根据this SO answer,实际情况并非如此,因此最终所有三种方法在性能方面似乎都相对相同。

使用第二种方法允许将该方法用作方法参考,这类似于在函数构造中使用多少其他标准库方法。例如stream.map(Math::abs)stream.map(String::toLowerCase)

总的来说,为什么使用第一种方法,它看起来(虽然最终不是)性能较差并且与其他示例不同?

And*_*eas 8

TL;DR UsingFunction.identity()仅创建一个对象,因此内存效率很高。


第三个实现不编译,因为T未定义,所以这不是一个选项。

在第二种实现中,每次编写时都会创建Function::identity一个新的对象实例。

在第一个实现中,无论何时调用Function.identity(),都会返回同一个lambda 对象的实例。

自己看很简单。首先identity在同一个类中创建这两个方法,因此将它们重命名为identity1identity2保持它们分别可识别。

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

static <T> T identity2(T in) {
    return in;
}
Run Code Online (Sandbox Code Playgroud)

编写一个test接受 aFunction并打印对象的方法,这样我们就可以看到它的唯一标识,正如哈希码所反映的那样。

static <A, B> void test(Function<A, B> func) {
    System.out.println(func);
}
Run Code Online (Sandbox Code Playgroud)

test重复调用该方法以查看每个方法是否获得了一个新的对象实例(我的代码在一个名为 的类中Test

test(Test.identity1());
test(Test.identity1());
test(Test.identity1());
test(Test::identity2);
test(Test::identity2);
for (int i = 0; i < 3; i++)
    test(Test::identity2);
Run Code Online (Sandbox Code Playgroud)

输出

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

static <T> T identity2(T in) {
    return in;
}
Run Code Online (Sandbox Code Playgroud)

如您所见,调用all 的多个语句Test.identity1()获取相同的对象,但使用all 的多个语句Test::identity2获取不同的对象。

确实,重复执行一条语句会获得相同的对象(如循环结果所示),但这与从不同语句获得的结果不同

结论: UsingTest.identity1()只创建一个对象,所以它比 using 更节省内存Test::identity2

  • 一个有趣的事实是,[`Objects.isNull(Object)`](https://docs.oracle.com/javase/8/docs/api/java/util/Objects.html#isNull-java.lang.Object- ) 不遵循 `Function.identity()` 的模式,而是旨在用作 `Object::isNull`。它已被添加到同一版本中,其文档甚至明确表示它打算与方法引用一起使用,就像 OP 的第二个变体一样。 (2认同)