简体   繁体   English

Scala逆变和协方差

[英]Scala contravariance and covariance

I am playing around with the typesystem of scala and found a strange case. 我正在玩scala的类型系统,发现了一个奇怪的案例。 I have a strong reason to believe, I don't understand covariant and covariance. 我有充分的理由相信,我不理解协变和协方差。

This is my problem case: 这是我的问题:

I have two classes, Point and ColorPoint, which is a subclass of Point. 我有两个类,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) 

This class casts B to A, while B should be a supertype of A: 此类将B转换为A,而B应为A的超类型:

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

This class casts B to A, while B should be a supertype of A: 此类将B转换为A,而B应为A的超类型:

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

Case 1: 情况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)

If I write this out: 如果我写出来:

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

I would expect this to be incorrect, because ColorPoint is not a supertype of Point, but scala doesn't complain. 我希望这是不正确的,因为ColorPoint不是Point的超类型,但scala不会抱怨。

Next one: 下一个:

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)

If I write this out: 如果我写出来:

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

I also expect this to be incorrect, but scala doesn't complain. 我也期望这是不正确的,但斯卡拉并没有抱怨。 I would say Point is not a subtype of ColorPoint. 我会说Point不是ColorPoint的子类型。

Is my reasoning correct or am I missing something? 我的推理是正确的还是我错过了什么?

I think you're misunderstanding what covariant and contravariant positions are. 我认为你误解了协变和逆变的立场。 It doesn't imply that you're able to cast between certain types, it just establishes the inheritance relationship between parameterized types. 这并不意味着你能够某种类型之间 ,它只是建立了参数化类型之间的继承关系。

You're only able to mark type parameters as in covariant or contravariant positions. 您只能将类型参数标记为协变或逆变位置。 When you say Container[+A] , you're saying that you can treat all instances of Container[A] as subtypes of Container[B] if A is a subtype of B . 当你说Container[+A] ,你说如果AB的子类型,你可以将Container[A]所有实例视为Container[B]的子类型。 This makes sense for immutable container classes: You can think of a List[Person] to be a parent of List[Employee] . 这对于不可变容器类是有意义的:您可以将List[Person]视为List[Employee]的父级。 Note this says nothing about casting rules -- those go unchanged. 请注意,这并未说明任何关于施法规则的内容

Contravariant is similar, but the opposite. 逆变是相似的,但相反。 If you have Writer[-A] , it says Writer[A] is a subtype of Writer[B] if B is a subtype of A . 如果您有Writer[-A]它说Writer[A]是的亚型Writer[B]如果B是的亚型A You can see how this makes intuitive sense too: If you have a Writer[Person] as something that can write a Person into some destination and you have Writer[Employee] as a writer that can only write Employees, it makes sense for Writer[Employee] to be a parent of Writer[Person] since writing a Person is a sub-task of writing a full Employee , even though it is the opposite for the types themselves. 您可以看到这也是如此直观的:如果您有一个Writer[Person]可以将Person写入某个目的地并且您将Writer[Employee]作为只能编写Employees的编写器,那么Writer[Employee]会有意义Writer[Employee]Writer[Person]的父级,因为编写Person是编写完整Employee的子任务,即使它与类型本身相反。

  1. asInstanceOf[T] ignores typechecks. asInstanceOf[T]忽略了typechecks。 So you may even have the following cast: 所以你甚至可能有以下演员:

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

    for any A and B . 任何AB

    Thus in your case Scala won't complain. 因此,在您的情况下,Scala不会抱怨。

  2. If I understand correctly, you want to have cast method only when the types are in proper relations (parent-child). 如果我理解正确,你只想在类型处于正确关系(父子)时才使用cast方法。 I think, you needn't have +/- in class declaration. 我想,你不需要+/-类声明。 Only two different casts: 只有两个不同的演员:

     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] } 

    this allows you to have the desired conversions. 这样您就可以获得所需的转化次数。

     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