斯卡拉逆变和协方差



我正在玩 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 

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

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

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

您只能将类型参数标记为协变或逆变位置。当你说Container[+A]时,你是说你可以将所有Container[A]实例视为Container[B]的子类型A如果它是B的子类型。这对于不可变的容器类是有意义的:您可以将List[Person]视为List[Employee]的父级。请注意,这并没有说明转换规则 - 这些规则保持不变。

逆变是相似的,但相反。如果你有Writer[-A],它说Writer[A]Writer[B]的子类型,如果BA的子类型。你也可以看到这是多么直观的意义:如果你有一个Writer[Person],可以将一个人写进某个目的地,而你Writer[Employee]是一个只能写员工的作家,那么Writer[Employee]成为Writer[Person]的父母是有意义的,因为写一个Person是写一个完整的Employee的子任务, 即使类型本身正好相反。

  1. asInstanceOf[T]忽略类型检查。所以你甚至可能有以下演员阵容:

    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
    

相关内容

  • 没有找到相关文章

最新更新