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

In Java why is Function.identity() a static method instead of something else?

Java 8 个添加的函数式编程构造,包括 Function class 及其关联的 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());

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

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

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

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

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

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

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

TL;DR 使用 Function.identity() 只创建一个对象,因此非常节省内存。


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

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

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

自己看很简单。首先在同一个 class 中创建两个 identity 方法,因此将它们重命名为 identity1identity2 以使它们能够单独识别。

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

static <T> T identity2(T in) {
    return in;
}

写一个 test 方法接受一个 Function 并打印对象,所以我们可以看到它的唯一身份,如哈希码所反映的那样。

static <A, B> void test(Function<A, B> func) {
    System.out.println(func);
}

重复调用test方法,看是否每一个都得到一个新的对象实例(我的代码在一个名为Test的class中).

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

输出

Test$$Lambda/0x0000000800ba0840@7adf9f5f
Test$$Lambda/0x0000000800ba0840@7adf9f5f
Test$$Lambda/0x0000000800ba0840@7adf9f5f
Test$$Lambda/0x0000000800ba1040@5674cd4d
Test$$Lambda/0x0000000800ba1440@65b54208
Test$$Lambda/0x0000000800ba1840@6b884d57
Test$$Lambda/0x0000000800ba1840@6b884d57
Test$$Lambda/0x0000000800ba1840@6b884d57

如您所见,多个 statements 调用 Test.identity1() 都得到相同的对象,但是多个 statements 使用 Test::identity2 都得到了不同的对象。

repeated 执行 same 语句确实得到相同的对象(如循环结果所示),但是这与从 different 语句获得的结果不同。

结论:使用Test.identity1()只创建一个对象,因此比使用Test::identity2更节省内存.