Scala 逆变和协变

标签 scala types covariance contravariance

我正在研究 scala 的类型系统,发现了一个奇怪的案例。我有充分的理由相信,我不懂协变和协方差。

这是我的问题案例:

我有两个类,Point 和 ColorPoint,它是 Point 的子类。

class Point(val x : Int, val y : Int)
class ColorPoint(x : Int, y : Int, val red : Int, val green : Int, val blue : Int) extends Point(x,y) 

这个类将 B 转换为 A,而 B 应该是 A 的父类(super class)型:

class CoVariance[+A]{
 def cast[B >: A](x : B) : A = {
  return x.asInstanceOf[A] 
 }
}

这个类将 B 转换为 A,而 B 应该是 A 的父类(super class)型:

class ContraVariance[-A]{
 def cast[B, A <: B](x : B) : A = {
    return x.asInstanceOf[A]
 }
}

案例一:

val co = new CoVariance[Point]
val color_point = new ColorPoint(1,2,3,4,5)
val point_co = co.cast(color_point) 
println(point_co.x)

如果我写出来:

// Covariance[Point] -> 
// cast[B :> Point](x : B) : Point -> (fill in ColorPoint)
// Cast[ColorPoint :> Point] : Point 

我希望这是不正确的,因为 ColorPoint 不是 Point 的父类(super class)型,但 scala 不会提示。

下一个:

val contra = new ContraVariance[Point]
val color_point_contra = new ColorPoint(1,2,3,4,5)
val point_contra = contra.cast(color_point_contra) 
println(point_contra.x)

如果我写出来:

// ContraVariance[Point] -> 
// cast[B, Point <: B](x : B) : Point -> (fill in ColorPoint)
// cast[ColorPoint, Point <: ColorPoint] : Point 

我也希望这是不正确的,但 scala 不会提示。我会说 Point 不是 ColorPoint 的子类型。

我的推理正确还是我遗漏了什么?

最佳答案

我认为您误解了什么是协变和逆变位置。这并不意味着您可以在某些类型之间转换,它只是在参数化类型之间建立继承关系。

您只能将类型参数标记为协变或逆变位置。当你说 Container[+A] ,你是说你可以处理 Container[A] 的所有实例作为 Container[B] 的子类型如果AB 的子类型.这对于不可变容器类是有意义的:你可以想到一个 List[Person]成为 List[Employee] 的 parent .请注意,这并没有说明类型转换规则——那些没有改变。

逆变类似,但相反。如果你有 Writer[-A] , 它说 Writer[A]Writer[B] 的子类型如果BA 的子类型.您也可以看出这在直觉上是多么有意义:如果您有一个 Writer[Person]作为可以将一个人写入某个目的地的东西,你有 Writer[Employee]作为一个只能写 Employees 的作者,这对于 Writer[Employee] 是有意义的成为 Writer[Person] 的 parent 自从写了一个Person是编写完整 Employee 的子任务,即使它与类型本身相反。

关于Scala 逆变和协变,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19668353/

相关文章:

scala - 为什么在 val 变量上使用 += 和 a=x+y 时会出现不同的错误消息?

java - 获取 UTC 时间并格式化,无毫秒

function - 为什么Function [-A1,…,+ B]与允许任何父类(super class)型作为参数无关?

java - 如何在Java中理解有界通配符?

scala - Scala for 循环内的赋值

scala - 删除 scala 中 int 对向量中的重复映射

scala - 在 scala 中函数的参数中的依赖类型和依赖者

haskell - 将数据构造函数映射到类型

python - 在 Python 中使用 7.0 或 float(7) 是否计算量较小?

c# - 接口(interface)中的协变和逆变