在像 Stream.reduce() 这样的 API 中选择不变性的充分理由是什么?
What are good reasons for choosing invariance in an API like Stream.reduce()?
回顾 Java 8 Stream
API 设计,我对 Stream.reduce()
参数的通用不变性感到惊讶:
<U> U reduce(U identity,
BiFunction<U,? super T,U> accumulator,
BinaryOperator<U> combiner)
同一个 API 的看似更通用的版本可能对 U
的个别引用应用了协变/逆变,例如:
<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);
解决方法,使用方法引用 "coerce" 将类型转换为目标类型:
double sum = stream.reduce(0.0, numberAdder::apply, numberAdder::apply);
C# 没有这个特殊问题,因为 Func(T1, T2, TResult)
定义如下,使用声明站点变量,这意味着任何使用 Func
的 API 都会获得此行为免费:
public delegate TResult Func<in T1, in T2, out TResult>(
T1 arg1,
T2 arg2
)
现有设计相对于建议的设计有哪些优势(可能还有 EG 决定的原因)?
或者,换句话说,我可能忽略了建议设计的哪些注意事项(例如类型推理困难、并行化约束或特定于归约操作的约束,例如关联性、对未来的预期Java 在 BiFunction<in T, in U, out R>
、...)?
上的声明站点差异
在我看来,只是提议的 增强 没有真正的用例。提议的 Javadoc 多了 3 个类型参数和 5 个通配符。我想将整个事情简化为官方 API 就足够了,因为常规 Java 开发人员不希望(通常甚至不能)为了让编译器高兴而失去理智。仅作记录,您的 reduce()
在类型签名中只有 165 个字符。
此外,.reduce()
的参数通常以 lambda 表达式的形式提供,因此当此类表达式通常不包含或非常简单的业务逻辑,因此只使用一次。
例如,我是您出色的 jOOQ 库的用户,也是一个好奇的 Java 喜欢泛型谜题的开发人员,但我常常怀念 SQL 元组的简单性由于 Result<T>
中的类型参数以及它在处理记录类型的接口时产生的那种麻烦,当我不得不在自己的接口中放置通配符时——并不是说这是一个 jOOQ 错误
遍历 lambda 开发的历史并找出做出此决定的 "THE" 原因是困难的 - 因此最终,人们将不得不等待其中一位开发人员回答这个问题。
一些提示可能如下:
流接口经历了多次迭代和重构。在Stream
接口的最早版本之一中,已经有专门的reduce
方法,而问题中最接近reduce
方法的那个仍然被称为[=35= 】 当年。这个已经收到一个 BinaryOperator
作为 combiner
参数。
有趣的是,很长一段时间以来,lambda 提案都包含一个专用接口 Combiner<T,U,R>
。与直觉相反,这并没有用作 Stream#reduce
函数中的 combiner
。相反,它被用作 reducer
,这似乎就是现在所说的 accumulator
。但是,Combiner
接口是 replaced with BiFunction
in a later revision.
与此处问题最显着的相似之处是 thread about the Stream#flatMap
signature at the mailing list,然后将其转换为关于流方法签名方差的一般问题。他们在某些地方修复了这些问题,例如
As Brian correct me:
<R> Stream<R> flatMap(Function<? super T, ? extends Stream<? extends R>>
mapper);
instead of:
<R> Stream<R> flatMap(Function<T, Stream<? extends R>> mapper);
但注意到在某些地方,这是不可能的:
T reduce(T identity, BinaryOperator<T> accumulator);
and
Optional<T> reduce(BinaryOperator<T> accumulator);
Can't be fixed because they used 'BinaryOperator', But if 'BiFunction' is
used then we have more flexibility
<U> U reduce(U identity, BiFunction<? super U, ? super T, ? extends U>
accumulator, BinaryOperator<U> combiner)
Instead of:
<U> U reduce(U identity, BiFunction<U, ? super T, U> accumulator, BinaryOperator<U> combiner);
Same comment regarding 'BinaryOperator'
(本人强调)
我发现 不 将 BinaryOperator
替换为 BiFunction
的唯一理由最终在 response to this statement, in the same thread 中给出:
BinaryOperator will not be replaced by BiFunction even if, as you said,
it introduce more flexibility, a BinaryOperator ask that the two parameters
and the return type to be the same so it has conceptually more weight
(the EG already votes on that).
也许有人可以挖掘出管理该决定的专家组投票的具体参考,但也许这句话已经充分回答了为什么会这样的问题...
回顾 Java 8 Stream
API 设计,我对 Stream.reduce()
参数的通用不变性感到惊讶:
<U> U reduce(U identity,
BiFunction<U,? super T,U> accumulator,
BinaryOperator<U> combiner)
同一个 API 的看似更通用的版本可能对 U
的个别引用应用了协变/逆变,例如:
<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);
解决方法,使用方法引用 "coerce" 将类型转换为目标类型:
double sum = stream.reduce(0.0, numberAdder::apply, numberAdder::apply);
C# 没有这个特殊问题,因为 Func(T1, T2, TResult)
定义如下,使用声明站点变量,这意味着任何使用 Func
的 API 都会获得此行为免费:
public delegate TResult Func<in T1, in T2, out TResult>(
T1 arg1,
T2 arg2
)
现有设计相对于建议的设计有哪些优势(可能还有 EG 决定的原因)?
或者,换句话说,我可能忽略了建议设计的哪些注意事项(例如类型推理困难、并行化约束或特定于归约操作的约束,例如关联性、对未来的预期Java 在 BiFunction<in T, in U, out R>
、...)?
在我看来,只是提议的 增强 没有真正的用例。提议的 Javadoc 多了 3 个类型参数和 5 个通配符。我想将整个事情简化为官方 API 就足够了,因为常规 Java 开发人员不希望(通常甚至不能)为了让编译器高兴而失去理智。仅作记录,您的 reduce()
在类型签名中只有 165 个字符。
此外,.reduce()
的参数通常以 lambda 表达式的形式提供,因此当此类表达式通常不包含或非常简单的业务逻辑,因此只使用一次。
例如,我是您出色的 jOOQ 库的用户,也是一个好奇的 Java 喜欢泛型谜题的开发人员,但我常常怀念 SQL 元组的简单性由于 Result<T>
中的类型参数以及它在处理记录类型的接口时产生的那种麻烦,当我不得不在自己的接口中放置通配符时——并不是说这是一个 jOOQ 错误
遍历 lambda 开发的历史并找出做出此决定的 "THE" 原因是困难的 - 因此最终,人们将不得不等待其中一位开发人员回答这个问题。
一些提示可能如下:
流接口经历了多次迭代和重构。在
Stream
接口的最早版本之一中,已经有专门的reduce
方法,而问题中最接近reduce
方法的那个仍然被称为[=35= 】 当年。这个已经收到一个BinaryOperator
作为combiner
参数。有趣的是,很长一段时间以来,lambda 提案都包含一个专用接口
Combiner<T,U,R>
。与直觉相反,这并没有用作Stream#reduce
函数中的combiner
。相反,它被用作reducer
,这似乎就是现在所说的accumulator
。但是,Combiner
接口是 replaced withBiFunction
in a later revision.与此处问题最显着的相似之处是 thread about the
Stream#flatMap
signature at the mailing list,然后将其转换为关于流方法签名方差的一般问题。他们在某些地方修复了这些问题,例如As Brian correct me:
<R> Stream<R> flatMap(Function<? super T, ? extends Stream<? extends R>> mapper);
instead of:
<R> Stream<R> flatMap(Function<T, Stream<? extends R>> mapper);
但注意到在某些地方,这是不可能的:
T reduce(T identity, BinaryOperator<T> accumulator);
and
Optional<T> reduce(BinaryOperator<T> accumulator);
Can't be fixed because they used 'BinaryOperator', But if 'BiFunction' is used then we have more flexibility
<U> U reduce(U identity, BiFunction<? super U, ? super T, ? extends U> accumulator, BinaryOperator<U> combiner)
Instead of:
<U> U reduce(U identity, BiFunction<U, ? super T, U> accumulator, BinaryOperator<U> combiner);
Same comment regarding 'BinaryOperator'
(本人强调)
我发现 不 将 BinaryOperator
替换为 BiFunction
的唯一理由最终在 response to this statement, in the same thread 中给出:
BinaryOperator will not be replaced by BiFunction even if, as you said, it introduce more flexibility, a BinaryOperator ask that the two parameters and the return type to be the same so it has conceptually more weight (the EG already votes on that).
也许有人可以挖掘出管理该决定的专家组投票的具体参考,但也许这句话已经充分回答了为什么会这样的问题...