关于 Java 和 Scala 中的泛型

Posted

技术标签:

【中文标题】关于 Java 和 Scala 中的泛型【英文标题】:About generics in Java and Scala 【发布时间】:2012-05-21 07:01:03 【问题描述】:

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

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

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

【问题讨论】:

我不太明白你的问题是什么意思。 在 Scala 中,我们编写 C[A]C[B],不,它们不是彼此的子类型默认情况下。仅当您将C 声明为class C[+T] 时,如果T 仅出现在C 的协变位置,编译器将允许您这样做。 @OliCharlesworth 最后,我的问题是如果类型 A 是 B 的子类型,泛型类型 C[A] 也是 C[B] 的子类型,那么这种泛型类型的属性是什么? @n.m.感谢您更正语法。您对 C[+T] 类的解释是正确的。 @user1391576:你的问题是:ArrayList<Base> is not a subtype of ArrayList<Derived>,但我猜你想问的是ArrayList<Derived> is not a subtype of ArrayList<Base>,不是吗? 【参考方案1】:

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

其次:你对 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 中,您有声明站点差异。也就是说,类型参数的方差与参数一起声明(使用注释+-):

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)
                  ^

然而,列表被声明为协变的:

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>

See also identifier &lt;:&lt;

【讨论】:

以上是关于关于 Java 和 Scala 中的泛型的主要内容,如果未能解决你的问题,请参考以下文章

请教关于java的泛型方法

Java数组中的泛型

请教一个关于java泛型的问题

Scala的泛型

泛型的泛型的好处

Scala的泛型