请参阅以下代码段:
trait Fruit {
val color:String
def == (fruit:Fruit) = this.color == fruit.color
}
case class Orange(color:String) extends Fruit
case class Apple(color:String) extends Fruit
正如所料,Orange("red") == Orange("red")
是true
。但是,我想强制说只能比较相同类型的水果,所以例如Orange("red") == Apple("red")
应该给出错误。我们能否以优雅的方式在特征==
的签名Fruit
中强制执行此操作?
编辑:我希望在编译时捕获错误,而不是在运行时捕获。
答案 0 :(得分:10)
Scalaz有一个Equal“类型类”可以解决这个问题,尽管操作员不同。
https://github.com/scalaz/scalaz/blob/master/core/src/main/scala/scalaz/Equal.scala
它的核心基本上是这个(虽然我使用===,他们使用一些unicode)
/** Defines a type safe === operator */
trait Equals[A] {
def ===(y : A) : Boolean
}
/** A conventient way to define Equals traits based on the == operator */
def equalA[A](x : A) = new Equals[A] {
def ===(y : A) = x == y
}
就像这样使用
// one for oranges
implicit val EqualsOrange = equalA[Orange] _
// one for apples
implicit val EqualsApple = equalA[Apple] _
Orange("red") === Orange("red") // true
Orange("red") === Orange("green") // false
Orange("red") === Apple("red") // Compile error
答案 1 :(得分:2)
不幸的是,你无法静态检查...至少,不使用==
,它使用Java的Object#equals
方法,其中所有内容都是根据原始对象强调定义的。
如果你想要类型安全,那么你唯一的选择就是实现另一个运算符,可能是类似=|=
的运算符,然后将它与类型类结合起来以确保你的安全。
我相信scalaz对于类型安全的平等也有一些有用的东西,但是不能很好地了解这个库以确定这一点。
您可以采用的另一种方法是在运行时保证安全,即使用canEqual
模式as described here。这已经被case类使用,并提供了一种很好的方法,可以在适当的时候选择性地破解LSP。
答案 2 :(得分:2)
如果您乐意将方法的名称从==更改为其他内容,我们可以这样做:
trait Fruit {
type FruitType <: Fruit
val color:String
def === (fruit:FruitType) = this.color == fruit.color
}
case class Orange(color:String) extends { type FruitType = Orange } with Fruit
case class Apple(color:String) extends {type FruitType = Apple } with Fruit
然后,如果我们将苹果与橘子进行比较,我们得到:
Apple("red") === Orange("red")
<console>:11: error: type mismatch;
found : Orange
required: Apple
Apple("red") === Orange("red")
和我们得到相同颜色苹果的苹果:
Apple("red") === Apple("red")
res10: Boolean = true
和我们得到不同颜色苹果的苹果:
Apple("green") === Apple("red")
res11: Boolean = false
答案 3 :(得分:1)
尝试:
trait Fruit {
val color: String
def == (fruit: Fruit) = getClass == fruit.getClass && color == fruit.color
}