简体   繁体   English

关于Java和Scala中的泛型

[英]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中,如果类型AB的子类型,则泛型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中,如果类型AB的子类型,则通用类型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 爪哇

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? 一个好的面试者会问这个问题: 会发生什么?

Scala 斯卡拉

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具有两个类型参数, IO分别是反变量和协变量。 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)

Why not ask the compiler? 为什么不问编译器呢?

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>

See also identifier <:< 另请参见标识符<:<

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM