如何定义任何扩展此特征的东西

时间:2011-03-08 22:54:37

标签: scala scala-2.8

请参阅以下代码段:

 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中强制执行此操作?

编辑:我希望在编译时捕获错误,而不是在运行时捕获。

4 个答案:

答案 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
 }