繁体   English   中英

Scala逆变和协方差

[英]Scala contravariance and covariance

我正在玩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的超类型:

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

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

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

情况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)

如果我写出来:

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

我希望这是不正确的,因为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)

如果我写出来:

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

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

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

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

您只能将类型参数标记为协变或逆变位置。 当你说Container[+A] ,你说如果AB的子类型,你可以将Container[A]所有实例视为Container[B]的子类型。 这对于不可变容器类是有意义的:您可以将List[Person]视为List[Employee]的父级。 请注意,这并未说明任何关于施法规则的内容

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

  1. asInstanceOf[T]忽略了typechecks。 所以你甚至可能有以下演员:

     def cast[B](a:A):B = a.asInstanceOf[B] 

    任何AB

    因此,在您的情况下,Scala不会抱怨。

  2. 如果我理解正确,你只想在类型处于正确关系(父子)时才使用cast方法。 我想,你不需要+/-类声明。 只有两个不同的演员:

     implicit class CastToParent[A](a:A) { def cast[B >: A]:B = a.asInstanceOf[B] } implicit class CastToChild[A](a:A) { def cast[B <: A]:B = a.asInstanceOf[B] } 

    这样您就可以获得所需的转化次数。

     trait A trait B extends A trait C val a:A = new B {} val b = a.cast[B] //parent to child val a1 = b.cast[A] //child to parent. val c = a.cast[C] // don't compile 

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM