Scala协方差和协方差 [英] Scala contravariance and covariance

查看:155
本文介绍了Scala协方差和协方差的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用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)扩展Point(x,y)

此类将B强制转换为A,而B应该是A的超类型:

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

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

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

案例1:

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

如果我这样写:

  //协方差[点]- > 
// cast [B:>点](x:B):点-> (填写ColorPoint)
// Cast [ColorPoint:>点]:点

我希望这是不正确的,因为ColorPoint不是Point的超类型,但是斯卡拉并没有抱怨。



下一个:

  val相反=新的ContraVariance [Point] 
val color_point_contra =新的ColorPoint(1,2,3,4,5)
val point_contra =相反.cast(color_point_contra)
println(point_contra.x)

如果我这样写:

  // ContraVariance [Point]-> 
// cast [B,点< ;: B](x:B):点-> (填写ColorPoint)
// cast [ColorPoint,Point< ;: ColorPoint]:点



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



我的推理是正确的还是缺少什​​么?

解决方案

我认为您误解了协变和逆变的位置。这并不意味着您能够在某些类型之间进行 cast ,而只是建立参数化类型之间的继承关系。



只能将类型参数标记为协变或反变位置。当您说 Container [+ A] 时,是说您可以对待 Container [A] 的所有实例作为 Container [B] 的子类型,如果 A B 。这对于不可变的容器类是有意义的:您可以将 List [Person] 视为 List [Employee] 。请注意,这并没有说明强制转换规则-规则保持不变。如果您有 Writer [-A] ,则表示 Writer [A] Writer [B] 如果 B A 的子类型。您还可以看到这在直觉上也是有道理的:如果您有 Writer [Person] 作为可以将Person写入某个目的地的东西,并且您有 Writer [Employee] 作为只能写雇员的作家, Writer [Employee] 成为<$ c $的父母是有意义的c> Writer [Person] ,因为编写 Person 是编写完整的 Employee ,即使类型本身相反。


I am playing around with the typesystem of scala and found a strange case. 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.

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:

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:

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

Case 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.

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.

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. This makes sense for immutable container classes: You can think of a List[Person] to be a parent of 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. 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.

这篇关于Scala协方差和协方差的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆