[英]About generics in Java and Scala
I am confused by the generic subtyping. 我对通用子类型感到困惑。
In Java, if type A
is a subtype of B
, generic type C<A>
and C<B>
are invariant. 在Java中,如果类型
A
是B
的子类型,则泛型C<A>
和C<B>
是不变的。 For instance, ArrayList<Base>
is not a subtype of ArrayList<Derived>
. 例如,
ArrayList<Base>
不是ArrayList<Derived>
的子类型。
However, in Scala, generic type C<A>
and C<B>
are covariant if type A
is a subtype of B
. 但是,在Scala中,如果类型
A
是B
的子类型,则通用类型C<A>
和C<B>
是协变的。 So what's the property of generic class in Scala has but not in Java? 那么,Scala中的泛型类有什么属性,而Java中没有?
Firstly note that variance is a property of generic type parameters , not of the parameterized types themselves. 首先请注意,方差是泛型类型参数的属性,而不是参数化类型本身的属性。
Secondly: you are wrong about scala - type parameters are invariant by default. 其次:您对scala是错误的-默认情况下类型参数是不变的。 Let us investigate!
让我们调查一下!
Java has use-site variance annotations . Java具有使用地点差异注释 。 That is, you can declare methods like this:
也就是说,您可以这样声明方法:
boolean addAll(Collection<? extends T> c);
There is, however, one form of "parameterized type" (I use the term loosely) in which the type parameters are covariant: Java Arrays! 但是,有一种形式的“参数化类型”(我宽松地使用术语),其中类型参数是协变的:Java数组! (This is actually insane because Arrays are mutable and hence it is easy to circumvent the type system).
(这实际上是疯狂的,因为数组是可变的,因此很容易规避类型系统)。 Consider the following:
考虑以下:
public static void subvert(Object[] arr) { arr[0] = "Oh Noes!"; }
And then: 然后:
Integer[] arr = new Integer[1];
subvert(arr); //this call is allowed as arrays are covariant
Integer i = arr[0];
A good interview question this one: what happens? 一个好的面试者会问这个问题: 会发生什么?
In Scala, you have declaration-site variance . 在Scala中,您具有声明站点的variance 。 That is, the variance of a type parameter is declared alongside the parameter (using the annotations
+
and -
): 也就是说,类型参数的方差在参数旁边声明(使用注解
+
和-
):
trait Function1[-I, +O]
This says that the trait Function1
has two type parameters, I
and O
which are contra- and co-variant respectively. 这表示特征
Function1
具有两个类型参数, I
和O
分别是反变量和协变量。 If no +/-
annotation is declared , then the type parameter is invariant . 如果未声明
+/-
注释 ,则type参数是不变的 。 For example, Set is invariant in its type parameter: 例如,Set在其type参数中是不变的:
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 is however, declared as being covariant: 但是,列表被声明为协变的:
scala> def bar(list: List[Any]) = ()
bar: (list: List[Any])Unit
scala> List(1)
res6: List[Int] = List(1)
scala> bar(res6)
Another way of demonstrating this is to ask the compiler directly for subtype-evidence: 演示此问题的另一种方法是直接向编译器询问子类型证据:
scala> class Cov[+A]
defined class Cov
scala> implicitly[Cov[Int] <:< Cov[Any]]
res8: <:<[Cov[Int],Cov[Any]] = <function1>
But with an invariant type parameter 但是带有不变的类型参数
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]]
^
Lastly, contravariance: 最后,反差:
scala> class Con[-A]
defined class Con
scala> implicitly[Con[Any] <:< Con[Int]]
res10: <:<[Con[Any],Con[Int]] = <function1>
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.