关于Java和Scala中的泛型

ele*_*ven 15 generics scala subtyping

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

在Java中,如果type AB泛型类型的子类型C<A>并且C<B>是不变的.例如,ArrayList<Base>不是子类型ArrayList<Derived>.

但是,在Scala中,泛型类型C<A>,C<B>如果type A是子类型,则是协变的B.那么Scala中泛型类的属性是什么,而Java中却没有?

oxb*_*kes 42

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

其次:你错误的scala - 默认情况下类型参数是不变的.我们来调查吧!

Java的

Java具有使用站点差异注释.也就是说,你可以声明这样的方法:

boolean addAll(Collection<? extends T> c);
Run Code Online (Sandbox Code Playgroud)

但是,有一种形式的"参数化类型"(我使用松散术语),其中类型参数是协变的:Java数组!(这实际上是疯狂的,因为数组是可变的,因此很容易绕过类型系统).考虑以下:

public static void subvert(Object[] arr) { arr[0] = "Oh Noes!"; }
Run Code Online (Sandbox Code Playgroud)

然后:

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

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

斯卡拉

在Scala中,您有声明站点方差.也就是说,类型参数的方差与参数一起声明(使用注释+-):

trait Function1[-I, +O]
Run Code Online (Sandbox Code Playgroud)

这表示该性状Function1具有两个类型参数,I并且O它们分别是禁忌和共变体.如果未+/-声明注释,则type参数是不变的.例如,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)
                  ^
Run Code Online (Sandbox Code Playgroud)

但是,List被声明为协变:

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

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

scala> bar(res6)
Run Code Online (Sandbox Code Playgroud)

为什么不问编译器?

另一种证明这种情况的方法是直接向编译器询问子类型证据:

scala> class Cov[+A]
defined class Cov

scala> implicitly[Cov[Int] <:< Cov[Any]]
res8: <:<[Cov[Int],Cov[Any]] = <function1>
Run Code Online (Sandbox Code Playgroud)

但是使用不变的类型参数

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]]
                        ^
Run Code Online (Sandbox Code Playgroud)

最后,逆转:

scala> class Con[-A]
defined class Con

scala> implicitly[Con[Any] <:< Con[Int]]
res10: <:<[Con[Any],Con[Int]] = <function1>
Run Code Online (Sandbox Code Playgroud)

另见标识符 <:<