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

时间:2020-01-17 23:39:06

标签: java java-8 functional-programming 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());

但是,还有另一种结构方式:

// 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),而第二种和第三种方法则没有。根据{{​​3}},实际上并非如此,因此最终所有这三种方法在性能上似乎都是相对等效的。

使用第二种方法可以将该方法用作方法参考,这与在功能构造中使用多少其他标准库方法相似。例如。 stream.map(Math::abs)stream.map(String::toLowerCase)

总的来说,为什么要使用第一种方法,它看起来(虽然最终并非如此)表现不佳(尽管最终并非如此),并且与其他示例不同?

1 个答案:

答案 0 :(得分:9)

TL; DR 使用Function.identity()仅创建一个对象,因此具有很高的内存效率。


第三种实现无法编译,因为T是未定义的,因此不是一种选择。

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

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

很容易看到自己。首先在同一个类中创建两个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的类中)

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$1/0x0000000800ba0840@7adf9f5f
Test$$Lambda$1/0x0000000800ba0840@7adf9f5f
Test$$Lambda$1/0x0000000800ba0840@7adf9f5f
Test$$Lambda$2/0x0000000800ba1040@5674cd4d
Test$$Lambda$3/0x0000000800ba1440@65b54208
Test$$Lambda$4/0x0000000800ba1840@6b884d57
Test$$Lambda$4/0x0000000800ba1840@6b884d57
Test$$Lambda$4/0x0000000800ba1840@6b884d57

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

确实,相同语句的重复执行得到相同的对象(从循环的结果中可以看出),但这与从不同的语句。

结论: :使用Test.identity1()仅创建一个对象,因此比使用Test::identity2具有更高的内存效率。

相关问题