以下签名有效,并在Scala中常用:
The following signature is valid and commonly used in Scala:
trait Collection[A] { def reduceLeft [B >: A] (f: (B, A) => B): B }但是,由于>:是Java中 super 的Scala等价物,这是我的第一个想法转换此签名(将功能类型替换为 BiFunction 并使用Use-Site方差注释(也称为有界通配符))将是
However, since >: is the Scala equivalent of super in Java, my first idea to convert this signature (replacing the function type with BiFunction and making use of Use-Site variance annotations aka Bounded Wildcards) would be
interface Collection<A> { <B super A> B reduceLeft(BiFunction<? super B, ? super A, ? extends B> mapper) }但是哦不!编译器抱怨< B超级A> 中的 super 令牌,因为您不能使用下界类型变数!现在,我该如何用Java代码编写此方法,而无需回到Java世界中不存在泛型的时间?
But oh no! The compiler complains about the super token in <B super A> because you can't have lower-bounded type variables! Now how would I write this method in Java code without having to time-travel back to when generics didn't exist in the Java world?
是的,我知道您认为我可以使用 B扩展A ,但这不是同一件事,如我的实现所示:
Yes, I know that you think I could use B extends A, but that is not the same thing, as shown by my implementation:
public <R extends E> R reduceLeft(BiFunction<? super R, ? super E, ? extends R> mapper) { if (this.isEmpty()) { return null; } Iterator<E> iterator = this.iterator(); R first = iterator.next(); // doesn't work, but would if R was a super-type of E (R super E) while (iterator.hasNext()) { mapper.apply(first, iterator.next()); } return first; }相反,我不得不使用受限制的版本:
Instead, I had to use this slightly more restricted version:
public E reduceLeft(BiFunction<? super E, ? super E, ? extends E> mapper) { if (this.isEmpty()) { return null; } Iterator<E> iterator = this.iterator(); E first = iterator.next(); while (iterator.hasNext()) { first = mapper.apply(first, iterator.next()); } return first; }推荐答案
B>:Scala方法定义中的A 约束是必要的,因为:
The B >: A constraint in the Scala method definition is necessary because:
解决此差异冲突的技巧是引入 B 泛型类型。
The trick to work around this variance conflict is to introduce that B generic type.
现在,正如您所提到的,Java使用了使用站点差异,因此任何用Java编写的集合都是不变的。这也意味着使用 A 作为方法的返回类型(即互变位置)没有问题。因此,下面的定义应该足够了-不需要 B 类型:
Now, as you've mentioned, Java employs use-site variance, so any collection written in Java will be invariant. This also means that there's no problem using A as the return type of a method, i.e., in contravariant position. So, the definition below should be enough — no need for a B type:
interface Collection<A> { A reduceLeft(BiFunction<? super A, ? super A, ? extends A> reducer); }但是,如您所见,拥有 A 曾经是一个下界,然后一个上限是 A 本质上是不变的-不使用通配符界限是不可能的垂头丧气。这意味着我们可以简化签名(这非常类似于 Stream.reduce ):
However, as you can see, the net effect of having A once be a lower bound and then an upper bound is that A is basically invariant — it's impossible to benefit from the wildcard bounds without using downcasting. That means we can simplify the signature (which is pretty similar to Stream.reduce):
interface Collection<A> { A reduceLeft(BiFunction<A, A, A> reducer); }此外,类型 BiFunction< A,A, A> 在Java 8中已经存在,名称为 BinaryOperator< A> 。
Also, the type BiFunction<A, A, A>, is already present in Java 8 under the name BinaryOperator<A>.