为什么方法定义为“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]
是一个超类型列表[AnyVal]
。
但是说我有以下特质:
trait List [+ A] {
def cons(hd:A):List [A]
}
为什么 cons
参数类型错误?
为什么需要 def cons [B>:A](v:B):List [B]
例如:
val animal_list:List [Animal] =列表(老虎,狗)
如果我们打电话:
animal_list.cons(tiger)
自 Tiger<:Animal
,是不是 cons
遇到问题?由于 B
是 Tiger
和 A
是动物和
B>:A
不正确。
为什么 cons
的参数类型错误?
特质列表[+ A] {
def cons(hd :A):List [A]
}
编译器会报错:
协变类型A发生在值为hd
的类型A的逆变位置,因为方法参数计数为逆变位置, code> A 是协变的。
让我们设想这个方法声明将被编译。然后我们可以这样做:
$ $ p $ class ListImpl [A] extends List [A] {
override def cons(hd: A):列表[A] = ???
}
val字符串:List [String] = new ListImpl [String]
val值:List [Any] = strings // OK,因为List [String]< :List [Any](在List [A]中,A是协变的)
values.cons(13)// OK(??),因为值的静态类型是List [Any],所以cons的参数应该是任何和13符合类型任何
上面的最后一行真的好吗?我们在 values
上调用 cons
。 values
与 strings
相同, strings
是对象类型为 ListImpl [String]
。因此最后一行中的 cons
调用需要 String
参数,但我们正在传递 Int
,因为值
的静态类型为 List [Any]
和 Int
符合任何
。有什么是绝对错误的 - 哪一行是责怪?答案是: cons
方法声明。为了解决这个问题,我们必须从逆变位置(在 cons
声明中)删除协变类型参数 A
。或者,我们可以使 A
非协变。
...不包含 cons
碰到问题了吗?
trait List [+ A] {
def cons [B>:A ](v:B):List [B]
}
val animal_list:List [Animal] = List(tiger,dog)//我们假设List.apply和具体实现列表是在某处定义的。
否, 我假设 animal_list.cons(tiger)$ c
调用,
Animal
是常见的超类型 Dog
和 Tiger
,并且 dog
和 tiger
分别是 Dog
和 Tiger
的实例。
$ b $ < A
和 B
类型参数被实例化为 Animal
,所以 cons
方法的形式为:
def cons [Animal>:Animal](v:Animal):List [Animal]
code>
动物> ;:动物
约束因为:
超类型和子类型关系是自反的,这意味着类型
既是超类型也是自身的子类型。 [信息来源]
cons
的参数是 Tiger
,符合类型 Animal
,所以方法调用是类型正确的。
注意,如果你强制 B
将被实例化为 Tiger
,例如 animal_list.cons [Tiger](tiger)
,那么这个调用将不会是类型正确的,并且你会得到编译器错误。
参见类似的例子这里。
I am studying variance in scala right now, and I think I have a good understanding of contravariance. For example given trait List[-A]
, I know that List[Int]
is a supertype of List[AnyVal]
.
But say that I have the following trait:
trait List[+A] {
def cons(hd: A): List[A]
}
Why is cons
parameter type wrong?
Why it is necessary to have def cons[B >: A](v: B): List[B]
?
For example:
val animal_list: List[Animal] = List(tiger, dog)
if we call:
animal_list.cons(tiger)
since Tiger <: Animal
, doesn't cons
ran into problem? Since B
is Tiger
and A
is Animal
and B >: A
is not true.
Why is cons
's parameter type wrong?
trait List[+A] {
def cons(hd: A): List[A]
}
Compiler give you error:
covariant type A occurs in contravariant position in type A of value hd
because method parameters count as contravariant positions, but A
is covariant.
Let's imagine that this method declaration would compile. Then we could do:
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
Is the last line above really OK? We are calling cons
on values
. values
is the same as strings
, and strings
is object of type ListImpl[String]
. So cons
invocation in the last line is expecting String
argument, but we are passing Int
, because values
's static type is List[Any]
and Int
conforms to Any
. Something is definitely wrong here - which line is to blame? The answer is: cons
method declaration. To fix this issue, we have to remove covariant type parameter A
from contravariant position (in cons
declaration). Alternatively we can make A
non-covariant.
See also these questions: #1, #2.
... doesn't cons
ran into problem?
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.
No, animal_list.cons(tiger)
invocation is type-correct.
I assume that Animal
is common supertype of Dog
and Tiger
, and that dog
and tiger
are instances of Dog
and Tiger
respectively.
In animal_list.cons(tiger)
invocation, both A
and B
type parameters are instantiated to Animal
, so cons
method takes form of:
def cons[Animal >: Animal](v: Animal): List[Animal]
Animal >: Animal
constraint is satisfied because:
Supertype and subtype relationships are reflexive, which means a type is both a supertype and a subtype of itself. [source]
The argument to cons
is Tiger
which conforms to type Animal
, so the method invocation is type-correct.
Notice that if you force B
to be instantiated to Tiger
, like animal_list.cons[Tiger](tiger)
, then this invocation won't be type-correct, and you'll get compiler error.
See similar example here.
这篇关于为什么方法定义为“cons [B>:A](v:B)”接受不是A的超类型的类型的参数?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!