关于Java和Scala中的泛型

16

我对通用子类型感到困惑。

在Java中,如果类型A是类型B的子类型,那么泛型类型C<A>C<B>是不变的。例如,ArrayList<Base>不是ArrayList<Derived>的子类型。

然而,在Scala中,如果类型A是类型B的子类型,泛型类型C<A>C<B>是协变的。那么在Scala中泛型类具有什么特性而Java中没有呢?


1
我不太明白你的问题是什么意思。 - Oliver Charlesworth
4
在Scala中,我们会写C[A]C[B],但请注意,它们不会默认成为彼此的子类型。只有当你将C声明为class C[+T]时,它们才可能是子类型,而编译器只会在TC中只出现在协变位置时才允许这样做。 - n. m.
@OliCharlesworth 总之,我的问题是如果类型A是类型B的子类型,泛型类型C[A]也是C[B]的子类型,那么这种泛型类型的属性是什么? - eleven
@n.m. 感谢您纠正语法。您对C[+T]类的解释是正确的。 - eleven
@user1391576:你的问题说:“ArrayList<Base>不是ArrayList<Derived>的子类型”,但我猜你想问的是“ArrayList<Derived>不是ArrayList<Base>的子类型”,对吗? - user unknown
1个回答

42

首先请注意,方差是泛型类型参数的属性,而不是参数化类型本身的属性。

其次:你关于scala的看法是错的-类型参数默认情况下是不变的。让我们来研究一下!

Java

Java有使用位置方差注释。也就是说,您可以像这样声明方法:

boolean addAll(Collection<? extends T> c);

然而,有一种“参数化类型”(我使用这个术语并不严谨)中,类型参数是协变的:Java数组!(这实际上很疯狂,因为数组是可变的,因此很容易规避类型系统)。考虑以下示例:

public static void subvert(Object[] arr) { arr[0] = "Oh Noes!"; }

然后:

Integer[] arr = new Integer[1];
subvert(arr); //this call is allowed as arrays are covariant
Integer i = arr[0];

这是一个不错的面试问题:会发生什么?

Scala

在Scala中,你有声明点变异性。也就是说,类型参数的变异性是在参数旁声明的(使用注释+-):

trait Function1[-I, +O]

这段话表明特质Function1有两个类型参数,分别是逆变和协变的IO。如果没有声明+/-注释,则该类型参数是不变的。例如,Set在其类型参数上是不变的:

scala> def foo(set: Set[Any]) = ()
foo: (set: Set[Any])Unit

scala> Set(1)
res4: scala.collection.immutable.Set[Int] = Set(1)

scala> foo(res4)
<console>:10: error: type mismatch;
 found   : scala.collection.immutable.Set[Int]
 required: Set[Any]
Note: Int <: Any, but trait Set is invariant in type A.
You may wish to investigate a wildcard type such as `_ <: Any`. (SLS 3.2.10)
              foo(res4)
                  ^

然而,List被声明为协变:

scala> def bar(list: List[Any]) = ()
bar: (list: List[Any])Unit

scala> List(1)
res6: List[Int] = List(1)

scala> bar(res6)

为什么不问编译器呢?

另一种证明方式是直接向编译器询问子类型证据:

scala> class Cov[+A]
defined class Cov

scala> implicitly[Cov[Int] <:< Cov[Any]]
res8: <:<[Cov[Int],Cov[Any]] = <function1>

但是使用不变的类型参数

scala> class Inv[A]
defined class Inv

scala> implicitly[Inv[Int] <:< Inv[Any]]
<console>:9: error: Cannot prove that Inv[Int] <:< Inv[Any].
              implicitly[Inv[Int] <:< Inv[Any]]
                        ^

最后,逆变性:

scala> class Con[-A]
defined class Con

scala> implicitly[Con[Any] <:< Con[Int]]
res10: <:<[Con[Any],Con[Int]] = <function1>

此外还可查看标识符<:<


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接