Scala逆变和协方差

Edg*_*rks 3 types scala 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) 
Run Code Online (Sandbox Code Playgroud)

此类将B转换为A,而B应为A的超类型:

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

此类将B转换为A,而B应为A的超类型:

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

情况1:

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)
Run Code Online (Sandbox Code Playgroud)

如果我写出来:

// Covariance[Point] -> 
// cast[B :> Point](x : B) : Point -> (fill in ColorPoint)
// Cast[ColorPoint :> Point] : Point 
Run Code Online (Sandbox Code Playgroud)

我希望这是不正确的,因为ColorPoint不是Point的超类型,但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)
Run Code Online (Sandbox Code Playgroud)

如果我写出来:

// ContraVariance[Point] -> 
// cast[B, Point <: B](x : B) : Point -> (fill in ColorPoint)
// cast[ColorPoint, Point <: ColorPoint] : Point 
Run Code Online (Sandbox Code Playgroud)

我也期望这是不正确的,但斯卡拉并没有抱怨.我会说Point不是ColorPoint的子类型.

我的推理是正确的还是我错过了什么?

yan*_*yan 5

我认为你误解了协变和逆变的立场.这并不意味着你能够施放某些类型之间,它只是建立了参数化类型之间的继承关系.

您只能将类型参数标记为协变或逆变位置.当你说Container[+A],你说,你可以把所有的情况下,Container[A]作为亚型Container[B],如果A是的亚型B.这对于不可变的容器类是有意义的:你可以认为a List[Person]是一个父类List[Employee].请注意,这并未说明任何关于施法规则的内容

逆变是相似的,但相反.如果你有Writer[-A],它说Writer[A]Writer[B]if B的子类型的子类型A.您可以看到这也是如此直观的:如果您有一个Writer[Person]可以将Person写入某个目的地的东西,并且您拥有Writer[Employee]只能编写Employees的编写器,那么成为Writer[Employee]一个父类是有意义的,Writer[Person]因为写一个Person是写一个完整的子任务Employee,即使它与类型本身相反.