有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

为什么Java8的ToIntFunction<t>不扩展函数<t,Integer>

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

我想知道,Java8API设计人员选择将原语替代方案与函数完全分离是否有令人信服的原因?是否有证据表明他们考虑过这样做并决定反对?我猜类似的问题至少也适用于其他一些“特殊”功能接口,如Consumer(可能是Function<;T,Void>;)和供应商(功能<;无效,T>;)

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

如果ToIntFunction(以及其他基本的通用功能接口)与函数有这种关系,它允许用户在需要函数参数的地方使用它(想到的是与其他函数的组合,例如调用myFunction.compose(myIntFunction)或避免在API中编写多个专用函数,当上述自动(un)装箱实现足够时)

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


共 (2) 个答案

  1. # 1 楼答案

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

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

  2. # 2 楼答案

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

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

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

    Function<String, int> myFunction;
    

    但这是:

    Function<String, Integer> myFunction;
    

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

    这解释了为什么JDK 8中有大量的接口,比如FunctionIntFunction,后者使用原语类型作为参数

    Lambda Mailing List中讨论了这一点,表明专家组正在努力解决这一问题

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

    More generally: the philosophy behind having specialized primitive streams (e.g., IntStream) is fraught with nasty tradeoffs. On the one hand, it's lots of ugly code duplication, interface pollution, etc. On the other hand, any kind of arithmetic on boxed ops sucks, and having no story for reducing over ints would be terrible. So we're in a tough corner, and we're trying to not make it worse.

    Trick #1 for not making it worse is: we're not doing all eight primitive types. We're doing int, long, and double; all the others could be simulated by these. Arguably we could get rid of int too, but we don't think most Java developers are ready for that. Yes, there will be calls for Character, and the answer is "stick it in an int." (Each specialization is projected to ~100K to the JRE footprint.)

    Trick #2 is: we're using primitive streams to expose things that are best done in the primitive domain (sorting, reduction) but not trying to duplicate everything you can do in the boxed domain. For example, there's no IntStream.into(), as Aleksey points out. (If there were, the next question(s) would be "Where is IntCollection? IntArrayList? IntConcurrentSkipListMap?) The intention is many streams may start as reference streams and end up as primitive streams, but not vice versa. That's OK, and that reduces the number of conversions needed (e.g., no overload of map for int -> T, no specialization of Function for int -> T, etc.)

    也许,将来当我们在Java中使用Support for Value Types时,我们将能够摆脱(或者至少不再需要使用)这些接口

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