5

我现在正在研究scala的方差,我想我对逆变有很好的理解。例如给出trait List[-A]的,我知道那List[Int]是 的超类型List[AnyVal]

但是说我有以下特点:

trait List[+A] {
  def cons(hd: A): List[A]
}

为什么cons参数类型错误?

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

例如:

val animal_list: List[Animal] = List(tiger, dog)

如果我们打电话:

animal_list.cons(tiger)

因为Tiger <: Animal,没有cons遇到问题?因为BTigerAAnimalB >: A不是真的。

4

1 回答 1

6

为什么cons's 的参数类型错误?

trait List[+A] {
  def cons(hd: A): List[A]
}

编译器给你错误:
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

上面最后一行真的没问题吗?我们正在cons呼吁valuesvalues与 相同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.

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

我假设 that是andAnimal的常见超类型,并且and分别是and的实例。DogTigerdogtigerDogTiger

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

def cons[Animal >: Animal](v: Animal): List[Animal]

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

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

consto的参数Tiger符合 type Animal,因此方法调用是类型正确的。

请注意,如果您强制B实例化为Tiger, like animal_list.cons[Tiger](tiger),那么此调用将不是类型正确的,并且您将收到编译器错误。

在此处查看类似示例。

于 2016-05-20T00:41:37.250 回答