我定义了两个类型类:
trait WeakOrder[-X] { self =>
def cmp(x: X, y: X): Int
def max[Y <: X](x: Y, y: Y): Y = if (cmp(x, y) >= 0) x else y
def min[Y <: X](x: Y, y: Y): Y = if (cmp(x, y) <= 0) x else y
}
trait Lattice[X] { self =>
def sup(x: X, y: X): X
def inf(x: X, y: X): X
}
我想做以下事情:
trait TotalOrder[-X] extends Lattice[X] with WeakOrder[X] { self =>
def sup(x: X, y: X): X = max(x, y)
def inf(x: X, y: X): X = min(x, y)
}
但这是不可能的,因为逆变类型X
出现在协变位置(sup
和inf
的返回值)。
但是,从语义上讲,这是正确的:max
和min
类型为签名max[Y <: X](x: Y, y: Y): Y
可以对max
/ min
的返回值进行编码是两个论点之一。
我尝试执行以下操作:
trait TotalOrder[-X] extends Lattice[X] with WeakOrder[X] { self =>
def sup[Y <: X](x: Y, y: Y): Y = max(x, y)
def inf[Y <: X](x: Y, y: Y): Y = min(x, y)
}
但是,方法def sup[Y <: X](x: Y, y: Y): Y
无法继承def sup[X](x: X, y: X): X
。编译器抱怨类型签名不匹配。但是前者(具有现场方差注释)强加了比后者签名更强的类型限制。为什么前者不能继承后者呢?如何绕过TotalOrder[-X]
的逆变类型限制(从语义上讲,总顺序是逆变的)?
答案 0 :(得分:0)
这在语义上并不正确。应该从协变和逆变的定义中明确,但我试着举个例子:
假设我们有实体层次结构:
class Shape(s:Float)
class Circle(r:Float) extends Shape(Math.PI.toFloat * r * r)
让我们假设您可以创建逆变订单,就像您尝试的那样:
trait CircleOrder extends TotalOrder[Circle] {
// compare by r
}
trait ShapeOrder extends TotalOrder[Shape] {
// compare by s
}
根据对象的定义,Shape <: Circle
,
CircleOrder <: ShapeOrder
(CircleOrder是ShapeOrder的超类型)
假设我们有将CircleOrder作为参数的客户端 并用它来比较圈子:
def clientMethod(circleOrder:TotalOrder[Circle]) = {
val maxCircle = circleOrder.max(???, ???) // expected to return Circle
maxCircle.r // accessing field that is present only in circle
}
然后,根据继承的定义,应该可以通过 ShapeOrder而不是CircleOrder(请记住,ShapeOrder是子类型):
clientMethod(new ShapeOrder {/*...*/})
显然它不起作用,因为客户仍然希望命令返回Circles,而不是Shapes。
我认为在您的情况下,最合理的方法将使用常规泛型。
这是你如何确保类型安全,但它有点难看。
trait WeakOrder[-X] {
def cmp(x: X, y: X): Int
def max[T](x: X with T, y: X with T): T =
if (cmp(x, y) >= 0) x else y
def min[T](x: X with T, y: X with T): T =
if (cmp(x, y) <= 0) x else y
}
trait Lattice[X] {
def sup[T](x: X with T, y: X with T): T
def inf[T](x: X with T, y: X with T): T
}
trait TotalOrder[-X] extends Lattice[X] with WeakOrder[X] {
def sup[T](x: X with T, y: X with T): T = max(x, y)
def inf[T](x: X with T, y: X with T): T = min(x, y)
}