为什么像 "cons[B >: A](v: B)" 这样定义的方法接受不是 A 超类型的类型的参数?
Why method defined like "cons[B >: A](v: B)" accepts argument of type which is not supertype of A?
我现在正在研究 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
运行不成问题吗?因为B
是Tiger
A
是 Animal
而 B >: A
不是真的。
为什么cons
的参数类型不对?
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
上面最后一行真的可以吗?我们正在 values
上调用 cons
。 values
与 strings
相同,strings
是 ListImpl[String]
类型的对象。所以最后一行中的 cons
调用需要 String
参数,但我们传递的是 Int
,因为 values
的静态类型是 List[Any]
和 Int
符合 Any
。这里肯定有问题 - 应该归咎于哪一行?答案是:cons
方法声明。要解决此问题,我们必须从逆变位置(在 cons
声明中)删除协变类型参数 A
。或者我们可以使 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)
调用类型正确。
我假设 Animal
是 Dog
和 Tiger
的公共超类型,并且 dog
和 tiger
是 Dog
的实例和 Tiger
分别。
在 animal_list.cons(tiger)
调用中,A
和 B
类型参数都被实例化为 Animal
,因此 cons
方法采用以下形式:
def cons[Animal >: Animal](v: Animal): List[Animal]
Animal >: Animal
约束已满足,因为:
Supertype and subtype relationships are reflexive, which means a type
is both a supertype and a subtype of itself. [source]
cons
的参数是Tiger
,符合类型Animal
,所以方法调用是类型正确的。
请注意,如果您强制将 B
实例化为 Tiger
,例如 animal_list.cons[Tiger](tiger)
,那么此调用将不会是类型正确的,并且您将得到编译器错误.
参见类似示例 here。
我现在正在研究 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
运行不成问题吗?因为B
是Tiger
A
是 Animal
而 B >: A
不是真的。
为什么cons
的参数类型不对?
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
上面最后一行真的可以吗?我们正在 values
上调用 cons
。 values
与 strings
相同,strings
是 ListImpl[String]
类型的对象。所以最后一行中的 cons
调用需要 String
参数,但我们传递的是 Int
,因为 values
的静态类型是 List[Any]
和 Int
符合 Any
。这里肯定有问题 - 应该归咎于哪一行?答案是:cons
方法声明。要解决此问题,我们必须从逆变位置(在 cons
声明中)删除协变类型参数 A
。或者我们可以使 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)
调用类型正确。
我假设 Animal
是 Dog
和 Tiger
的公共超类型,并且 dog
和 tiger
是 Dog
的实例和 Tiger
分别。
在 animal_list.cons(tiger)
调用中,A
和 B
类型参数都被实例化为 Animal
,因此 cons
方法采用以下形式:
def cons[Animal >: Animal](v: Animal): List[Animal]
Animal >: Animal
约束已满足,因为:
Supertype and subtype relationships are reflexive, which means a type is both a supertype and a subtype of itself. [source]
cons
的参数是Tiger
,符合类型Animal
,所以方法调用是类型正确的。
请注意,如果您强制将 B
实例化为 Tiger
,例如 animal_list.cons[Tiger](tiger)
,那么此调用将不会是类型正确的,并且您将得到编译器错误.
参见类似示例 here。