Scala 协方差和下限我不明白
Scala covariance and lower bound i dont get it
我目前正在学习 scala,我对方差注释感到困惑,尤其是 协方差 和 逆变。
所以我做了一些研究,得出了以下示例
class Box[+T] {
def put[U >: T](x: U): List[U] = {
List(x)
}
}
class Animal {
}
class Cat extends Animal {
}
class Dog extends Animal {
}
var sb: Box[Animal] = new Box[Cat];
所以这表示 class Box 在 T 中是协变的,这意味着 Box[Cat] 是 Box[Animal] 的子class,因为 Cat 是 Animal 的子class。目前我明白这一点。但是当涉及到方法参数时,我的理解就结束了。规范说方法参数不能协变,所以我们必须使用这个下限注释。
让我们看一下方法定义
def put[U >: T](x: U): List[U] = {
List(x)
}
So [U >: T] says that U must be a superclass of T
尝试以下代码
var sb: Box[Animal] = new Box[Cat];
sb.put(new Cat);
按预期工作,但这让我抓狂
var sb: Box[Animal] = new Box[Cat];
sb.put(1);
逻辑上来说,将 INT 放入动物盒中是没有意义的,尽管它是正确的,因为 INT 将被解析为Any 是 Animal 的超级class。
所以我的问题是
我如何调整 put method
只接受子类型的代码
动物?我不能使用上限注释
class Box[+T] {
def put[U <: T](x: U): List[U] = {
List(x)
}
}
因为我得到了这个众所周知的错误
covariant type T occurs in contravariant position in type
您可以同时添加下限和上限:
class Box[+T] { def put[U >: T <: Animal](x: U): List[U] = List(x) }
但这不是您想要的,因为您将 Box
的定义连接到 Animal
并且逻辑上没有理由添加这样的上限。
你说:
It logically makes no sense to me to put an INT into a Box of Animals alltough its correct since INT will be resolved to Any which is a superclass of Animal.
你不把一个 Int
放到一个 Box[Animal]
中,现有的框是不可变的(并且不可能让它可变,因为协方差的定义不允许它).相反,您会得到一个新类型的盒子(或者在您的 put
方法的情况下)。如果您的目标只是获得 List[Anmial]
,那么您只需指定:
scala> class Box[+T] { def put[U >: T](x: U): List[U] = List(x) }
defined class Box
scala> var b: Box[Animal] = new Box[Cat]
b: Box[Animal] = Box@23041911
scala> val xs: List[Animal] = b put new Dog
xs: List[Animal] = List(Dog@f8d6ec4)
scala> val xs: List[Animal] = b put 1
<console>:14: error: type mismatch;
found : Int(1)
required: Animal
val xs: List[Animal] = b put 1
^
scala> val xs = b put 1 // this will result in a List[Any]
xs: List[Any] = List(1)
无需复杂化put
方法的定义。
有关为什么需要协变和逆变的更深入的解释,请参阅 this question。
我目前正在学习 scala,我对方差注释感到困惑,尤其是 协方差 和 逆变。
所以我做了一些研究,得出了以下示例
class Box[+T] {
def put[U >: T](x: U): List[U] = {
List(x)
}
}
class Animal {
}
class Cat extends Animal {
}
class Dog extends Animal {
}
var sb: Box[Animal] = new Box[Cat];
所以这表示 class Box 在 T 中是协变的,这意味着 Box[Cat] 是 Box[Animal] 的子class,因为 Cat 是 Animal 的子class。目前我明白这一点。但是当涉及到方法参数时,我的理解就结束了。规范说方法参数不能协变,所以我们必须使用这个下限注释。
让我们看一下方法定义
def put[U >: T](x: U): List[U] = {
List(x)
}
So [U >: T] says that U must be a superclass of T
尝试以下代码
var sb: Box[Animal] = new Box[Cat];
sb.put(new Cat);
按预期工作,但这让我抓狂
var sb: Box[Animal] = new Box[Cat];
sb.put(1);
逻辑上来说,将 INT 放入动物盒中是没有意义的,尽管它是正确的,因为 INT 将被解析为Any 是 Animal 的超级class。
所以我的问题是
我如何调整 put method
只接受子类型的代码
动物?我不能使用上限注释
class Box[+T] {
def put[U <: T](x: U): List[U] = {
List(x)
}
}
因为我得到了这个众所周知的错误
covariant type T occurs in contravariant position in type
您可以同时添加下限和上限:
class Box[+T] { def put[U >: T <: Animal](x: U): List[U] = List(x) }
但这不是您想要的,因为您将 Box
的定义连接到 Animal
并且逻辑上没有理由添加这样的上限。
你说:
It logically makes no sense to me to put an INT into a Box of Animals alltough its correct since INT will be resolved to Any which is a superclass of Animal.
你不把一个 Int
放到一个 Box[Animal]
中,现有的框是不可变的(并且不可能让它可变,因为协方差的定义不允许它).相反,您会得到一个新类型的盒子(或者在您的 put
方法的情况下)。如果您的目标只是获得 List[Anmial]
,那么您只需指定:
scala> class Box[+T] { def put[U >: T](x: U): List[U] = List(x) }
defined class Box
scala> var b: Box[Animal] = new Box[Cat]
b: Box[Animal] = Box@23041911
scala> val xs: List[Animal] = b put new Dog
xs: List[Animal] = List(Dog@f8d6ec4)
scala> val xs: List[Animal] = b put 1
<console>:14: error: type mismatch;
found : Int(1)
required: Animal
val xs: List[Animal] = b put 1
^
scala> val xs = b put 1 // this will result in a List[Any]
xs: List[Any] = List(1)
无需复杂化put
方法的定义。
有关为什么需要协变和逆变的更深入的解释,请参阅 this question。