当前位置: 首页 > 知识库问答 >
问题:

为什么Java8的toIntFunction不扩展函数

王彭薄
2023-03-14

如果我编写了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));
    }
}

我想知道,Java8 API设计者选择将原始的替代方案与函数完全分离,是不是有一个令人信服的原因?是否有证据显示他们曾考虑这样做,并决定不这样做?我想类似的问题至少也会出现在其他一些“特殊”功能接口上,比如Consumer(可能是function )和Supplier(function )。

我还没有对这件事的所有后果进行深入和彻底的思考,所以我可能遗漏了一些东西。

如果ToIntFunction(和其他基本的泛型函数接口)与Function有这种关系,那么就可以在需要Function参数的地方使用它(想到的是与其他函数的组合,例如调用MyFunction.compose(myIntFunction)或者避免在API中编写几个专门的函数,而如上所述的自动(取消)装箱实现就足够了)。

这与这个问题非常相似:为什么Java8的谓词 不扩展函数 但我已经意识到,由于语义原因,答案可能不同。因此,我重新表述了这个问题,它是函数的一个简单的原语替代,在这里不可能有任何语义,只有原语和包装类型,甚至可以消除包装对象为空的可能性。

共有1个答案

宗建章
2023-03-14

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

这意味着我们不能将基元类型与泛型一起使用,因此,我们被迫使用包装类型。

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

Function<String, int> myFunction;
Function<String, Integer> myFunction;

这样做的问题是装箱/取消装箱。这可能会变得昂贵,并使处理基元数据类型的算法难以优化,因为经常需要为基元值创建包装对象,反之亦然。

这就解释了为什么JDK 8中的接口激增,比如functionintfunction,后者使用基元类型作为参数。

在Lambda邮件列表中的某个时候讨论了这一点,表明专家组正在为此苦苦挣扎。

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

更普遍地说:拥有专门的原始流(例如,IntStream)背后的哲学充满了令人讨厌的权衡。一方面,这是大量丑陋的代码重复,接口污染等。另一方面,任何一种在盒形操作上的算术都是糟糕的,没有故事来减少超过int将是可怕的。所以我们现在处境艰难,我们不想让情况变得更糟。

不让它变得更糟的第一个诀窍是:我们没有做所有的八个原始类型。我们在做int,long和double;所有其他的都可以用这些来模拟。可以说,我们也可以摆脱int,但我们认为大多数Java开发人员还没有做好准备。是的,会有字符的调用,答案是“把它贴在int中。”(每个专门化都被投影到约100K的JRE占用空间。)

诀窍2是:我们使用原语流来公开在原语域中最好完成的事情(排序、约简),而不是试图复制在装箱域中可以完成的一切。例如,Aleksey指出,没有intStream.into()。(如果有的话,下一个问题是“IntCollection在哪里?IntArrayList在哪里?IntConcurrentSkipListMap在哪里?)目的是许多流可以作为引用流开始,以基元流结束,反之亦然。这是可以的,并且减少了所需转换的数量(例如,对于int->T,不重载映射,对于int->T不专门化函数,等等)

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

专家组纠结于几个设计问题,不仅仅是这个。保持向下兼容的需要、要求或约束使事情变得困难,然后我们就有了其他重要的条件,如缺少值类型、类型擦除和检查异常。如果Java有第一个而没有其他两个,那么JDK8的设计就会非常不同。因此,我们都必须明白,这是一个难题,有很多权衡,而EG必须在某处划出界线,并作出决定。

 类似资料:
  • 问题内容: 创建数组时,无法更改其大小;它是固定的。好的,看起来不错,我们可以创建一个新的更大的数组并逐个复制值,这有点慢。它的技术背景是什么? 问题答案: 这个问题没有提到语言,因此我将选择基于“ C”的数组作为答案。 数组被分配为单个内存块。增长数组是有问题的,因为正确执行此操作的唯一方法是在最后增长它。为了增加大小N,在数组的末尾必须在下一个分配的地址之前至少有N个空闲字节。 要支持这种类型

  • 问题内容: 事实并非如此,这让我感到惊讶。 我认为,如果这样声明,那将是很有意义的: 毕竟,是的集合,不是吗? 那么,为什么没有这样一个很好的实现呢? 感谢Cletus提供的最权威的答案,但是我仍然想知道为什么,如果你已经可以查看(通过),那么它不仅扩展了该接口。 如果a 是a ,则元素是什么?唯一合理的答案是“键值对” 确实会很棒! 但这提供了非常有限的(且不是特别有用的)Map抽象。 但是,如

  • 问题内容: 我想知道为什么在Java语言中a 不能扩展。 我不是在谈论一个延伸的(这不能做,因为Java没有多重继承,而Š隐含延长),但一类的以只添加额外的方法,而不是额外的枚举值。 就像是: 要这样使用: 因此,有人可以对此限制提供理由(或将我指向正确的JLS部分)吗? 问题答案: 我认为 他们这样做 的答案来自以下问题: 在您的示例中,如何实例化MyClass?用户永远不会(通过)显式实例化枚

  • 问题内容: 为什么接口不扩展接口?不是键值对的集合吗? 问题答案: 集合假定一个值的元素。映射假定键/值对的条目。他们本来可以重新使用相同的通用接口,但是它们实现的某些方法不兼容,例如 您可以将Map建模为条目的集合,这就是这样做的。 有一些共同的方法。,,,但这些都不太可能有多少价值作为一个独立的接口。(可以再次使用)

  • 我知道一些关于AbstractQueuedSynchronizer的详细信息。它是一个用于创建依赖于状态的类或同步器的框架。但我不理解在ThreadPoolExecutor的Worker中扩展这个类的意义。 由Worker类的签名可以推断出以下内容: > 提交新的可运行/可调用任务时,将创建一个新的工作对象。 Worker的新对象可以被视为新线程。 方法将添加新的worker(或只是一个任务)并在

  • 扩展是可定制化浏览体验的小程序,它们使用户可以根据个人需要或者偏好定制 Chrome 的功能和行为。它们基于 Web 技术(例如 HTML,JavaScript 和 CSS)构建。 扩展必须满足狭义定义且易于理解的单一目的(译者注:功能简单易懂化)。一个扩展可以包括多个组件和一系列功能,只要所有的内容都有助于实现共同的目标。 用户交互界面应尽量小且有意图。他们的范围从简单的图标,如右侧显示的 Go