7

如果我编写了 ToIntFunction 接口,我想在接口中编码它只是一个返回原始 int 的函数,如下所示:

@FunctionalInterface
public interface ToIntFunction<T> extends Function<T, Integer> {
    int applyAsInt(T value);

    @Override
    default Integer apply(T value) {
        return Integer.valueOf(applyAsInt(value));
    }
}

我想知道,Java 8 API 设计者选择将原始替代方案与 Function 完全分开是否有令人信服的理由?是否有证据表明他们考虑这样做并决定反对?我想类似的问题至少适用于其他一些“特殊”功能接口,如消费者(可能是 Function<T,Void>)和供应商(Function<Void,T>)。

我还没有深入和彻底地考虑过这一切的后果,所以我可能遗漏了一些东西。

如果 ToIntFunction(和其他原始通用函数接口)与 Function 有这种关系,它将允许在需要 Function 参数的地方使用它(想到的是与其他函数的组合,例如调用 myFunction.compose(myIntFunction)或者避免在 API 中编写几个专门的函数时,如上所述的这种自动(取消)装箱实现就足够了)。

这与这个问题非常相似:Why doesn't Java 8's Predicate<T> extend Function<T, Boolean>但我意识到由于语义原因,答案可能有所不同。因此,我正在为这种简单的原始替代函数的情况重新制定问题,其中不能有任何语义,只有原始类型与包装类型,甚至消除了 null 包装对象的可能性。

4

2 回答 2

16

JDK 8 中的接口爆炸是 Java 中一个小问题的产物:缺少值类型。

这意味着我们不能将原始类型与泛型一起使用,因此,我们不得不使用包装类型。

换句话说,这是不可能的:

Function<String, int> myFunction;

但这是:

Function<String, Integer> myFunction;

这个问题是装箱/拆箱。这可能会变得昂贵,并且由于不断需要为原始值创建包装器对象,因此处理原始数据类型的算法难以优化,反之亦然。

这就解释了为什么 JDK 8 中的接口爆炸式增长,比如Functionand IntFunction,后者使用原始类型作为参数。

Lambda 邮件列表中的某个时间点对此进行了讨论,表明专家组正在为此苦苦挣扎。

lambda 项目的规范负责人 Brian Goetz 在那里写道:

更一般地说:拥有专门的原始流(例如,IntStream)背后的哲学充满了令人讨厌的权衡。一方面,它有很多丑陋的代码重复接口污染等。另一方面,任何类型的盒装操作算术都很糟糕,而且没有减少整数的故事会很糟糕。所以我们处于一个艰难的角落,我们正在努力不让情况变得更糟。

不让它变得更糟的技巧 #1 是:我们没有做所有八种原始类型。我们正在做 int、long 和 double;所有其他的都可以用这些来模拟。可以说我们也可以摆脱 int,但我们认为大多数 Java 开发人员还没有为此做好准备。是的,会有对 Character 的调用,答案是“将其放入 int 中”。(预计每个专业化的 JRE 足迹约为 100K。)

技巧 #2 是:我们使用原始流来公开在原始域中最好完成的事情(排序、归约),而不是试图复制您在盒装域中可以做的所有事情。例如,正如 Aleksey 指出的那样,没有 IntStream.into()。(如果有,下一个问题将是“IntCollection 在哪里?IntArrayList?IntConcurrentSkipListMap?)其意图是许多流可能从参考流开始并最终作为原始流,但反之亦然。没关系,那减少了所需的转换次数(例如,int -> T 的 map 没有重载,int -> T 的 Function 没有特化等)

也许,将来当我们在 Java 中获得对值类型的支持时,我们将能够摆脱(或至少不再需要使用)这些接口。

专家组在几个设计问题上苦苦挣扎,而不仅仅是这个。保持向后兼容性的需要、要求或约束使事情变得困难,然后我们还有其他重要条件,例如缺少值类型、类型擦除和检查异常。如果 Java 有第一个而没有其他两个,那么 JDK 8 的设计将会非常不同。所以,我们都必须明白,这是一个需要大量权衡的难题,EG 必须在某处划清界限并做出决定。

于 2014-03-27T15:05:48.103 回答
2

因为这意味着所有原始操作功能都会产生自动装箱和拆箱操作的成本。

如果您不关心这对性能的影响,只需使用盒装版本的Function<>.

于 2014-03-27T14:46:42.337 回答