为什么定义为"cons [B>:A](v:B)"的方法接受的类型的参数不是A的超类型?

tes*_*ing 5 generics scala covariance contravariance generic-method

我现在正在研究斯卡拉的变异,我认为我对逆变有很好的理解.例如trait List[-A],我知道这List[Int]是一个超类型List[AnyVal].

但是说我有以下特点:

trait List[+A] {
  def cons(hd: A): List[A]
}
Run Code Online (Sandbox Code Playgroud)

为什么cons参数类型错误?

为什么有必要def cons[B >: A](v: B): List[B]

例如:

val animal_list: List[Animal] = List(tiger, dog)
Run Code Online (Sandbox Code Playgroud)

如果我们打电话:

animal_list.cons(tiger)
Run Code Online (Sandbox Code Playgroud)

既然Tiger <: Animal,不会cons遇到问题?既然BTiger,A现在AnimalB >: A不是真的.

TeW*_*eWu 6

为什么cons参数类型错误?

trait List[+A] {
  def cons(hd: A): List[A]
}
Run Code Online (Sandbox Code Playgroud)

编译器会给出错误:
covariant type A occurs in contravariant position in type A of value hd
因为方法参数计为逆变位置,但是A是协变的.

让我们假设这个方法声明会编译.然后我们可以这样做:

class ListImpl[A] extends List[A] {
  override def cons(hd: A): List[A] = ???
}

val strings: List[String] = new ListImpl[String]
val values: List[Any] = strings // OK, since List[String] <: List[Any] (in List[A], A is covariant)
values.cons(13) // OK(??), since values's static type is List[Any], so argument of cons should be Any, and 13 conforms to type Any
Run Code Online (Sandbox Code Playgroud)

上面的最后一行真的好吗?我们呼吁consvalues.values与...相同strings,strings是类型的对象ListImpl[String].所以cons最后一行中的调用是期望String参数,但我们正在传递Int,因为values静态类型是List[Any]并且Int符合Any.这里肯定是错的 - 哪条线应该受到责备?答案是:cons方法声明.要解决此问题,我们必须A从逆变位置(cons声明中)中删除协变类型参数.或者,我们可以使A非协变.

另请参阅以下问题:#1,#2.

......没cons遇到问题?

trait List[+A] {
  def cons[B >: A](v: B): List[B]
}

val animal_list: List[Animal] = List(tiger, dog)  // We are assuming that List.apply and concrete implementation of List is somewhere defined.
Run Code Online (Sandbox Code Playgroud)

不,animal_list.cons(tiger)调用是类型正确的.

我以为Animal是普通超DogTiger,和dogtiger的实例DogTiger分别.

animal_list.cons(tiger)调用中,两个AB类型参数都被实例化Animal,因此cons方法采用以下形式:

def cons[Animal >: Animal](v: Animal): List[Animal]
Run Code Online (Sandbox Code Playgroud)

Animal >: Animal 约束得到满足,因为:

超类型和子类型关系是自反的,这意味着类型既是超类型又是自身的子类型.[ 来源 ]

参数consTiger符合类型的Animal,因此方法调用是类型正确的.

请注意,如果你强迫B被实例化Tiger,喜欢animal_list.cons[Tiger](tiger),那么这个调用将不会是类型正确的,你会得到编译器错误.

看到类似的例子在这里.