java - 在Stream.reduce()这样的API中选择不变性有什么好理由?

回顾Java 8 Stream API设计,我惊讶于Stream.reduce()参数的泛化不变性:

<U> U reduce(U identity,
             BiFunction<U,? super T,U> accumulator,
             BinaryOperator<U> combiner)

同一个API的一个似乎更通用的版本可能已经对单个引用应用了协方差/逆变,例如:
<U> U reduce(U identity,
             BiFunction<? super U, ? super T, ? extends U> accumulator,
             BiFunction<? super U, ? super U, ? extends U> combiner)

这将允许目前无法实现的以下功能:
// Assuming we want to reuse these tools all over the place:
BiFunction<Number, Number, Double> numberAdder =
    (t, u) -> t.doubleValue() + u.doubleValue();

// This currently doesn't work, but would work with the suggestion
Stream<Number> stream = Stream.of(1, 2L, 3.0);
double sum = stream.reduce(0.0, numberAdder, numberAdder);

解决方法,使用方法引用将类型“强制”为目标类型:
double sum = stream.reduce(0.0, numberAdder::apply, numberAdder::apply);

C没有这个特殊的问题,因为使用声明站点方差,定义如下,这意味着任何使用U的API都可以免费获得这个行为:
public delegate TResult Func<in T1, in T2, out TResult>(
    T1 arg1,
    T2 arg2
)

与建议的设计相比,现有设计的优势(以及可能的原因,如决策)是什么?
或者,以不同的方式询问,我可能忽略的建议设计的警告(例如类型推断困难、并行化约束或特定于还原操作的约束,例如关联性、对未来Java声明站点方差的预期)。Func(T1, T2, TResult),…)?

最佳答案

在lambda开发的历史中爬行并隔离这个决策的“原因”是很困难的-所以最终,必须等待开发人员之一来回答这个问题。
一些提示可能如下:
流接口经历了多次迭代和重构。在Stream接口的最早版本中,有专门的reduce方法,而最接近问题中的reduce方法的方法在当时仍被称为Stream#fold。这一个已经收到一个BinaryOperator作为combiner参数。
有趣的是,在相当长的一段时间内,lambda方案包括一个专用的接口。与直觉相反,这并没有被用作Combiner<T,U,R>函数中的combiner。相反,它被用作Stream#reduce,这似乎就是现在所说的reducer。然而,accumulator界面是replaced with Combiner in a later revision
与这里的问题最显著的相似性在athread about the BiFunction signature at the mailing list中发现,然后将其转化为有关流方法签名的方差的一般问题。例如,他们在某些地方修复了这些
布赖恩纠正我时:
Stream#flatMap
而不是:
<R> Stream<R> flatMap(Function<? super T, ? extends Stream<? extends R>> mapper);
但注意到在某些地方,这是不可能的:
<R> Stream<R> flatMap(Function<T, Stream<? extends R>> mapper);

T reduce(T identity, BinaryOperator<T> accumulator);
无法修复,因为它们使用了“BinaryOperator”,但如果“BifFunction”是
使用后我们有更多的灵活性
Optional<T> reduce(BinaryOperator<T> accumulator);
而不是:
<U> U reduce(U identity, BiFunction<? super U, ? super T, ? extends U> accumulator, BinaryOperator<U> combiner)
关于“binaryOperator”的相同注释
(我强调)。
我找到的唯一不将<U> U reduce(U identity, BiFunction<U, ? super T, U> accumulator, BinaryOperator<U> combiner);替换为BinaryOperator的理由最终在response to this statement, in the same thread中给出:
binaryOperator不会被biffunction替换,即使如您所说,
它引入了更多的灵活性,一个二元运算符要求两个参数
返回类型是相同的,因此在概念上具有更大的权重
(EG已经对此投票了)。
也许有人能从管理这一决定的专家组的投票中找出一个精确的参考,但也许这句话已经充分回答了为什么它是这样的问题……

本文翻译自 https://stackoverflow.com/questions/35680706/

网站遵循 CC BY-SA 4.0 协议,转载或引用请注明出处。

标签 java java-stream covariance contravariance invariance


相关文章:

java - Hibernate验证注释 - 验证至少一个字段不为空

java - 我如何反序列化Jackson的秒数时间戳?

java - 如何获得两个ZonedDateTime实例的最大值?

c# - 演员表<T>-协方差/矛盾性问题

c# - 覆盖虚拟方法时缺少返回类型协方差的解决方法

java - 单元测试和太多模拟

java - 在Android API级别<10中,String方法isEmpty是否真的不可用?

java - 在参数中给定多个条件时过滤流

java - 流的应用方法

c# - 与泛型的对立/协方差-无法分配给T