Как определить все, что расширяет эту черту - PullRequest
4 голосов
/ 09 марта 2011

См. Следующий фрагмент кода:

 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 ]

10 голосов
/ 09 марта 2011

Scalaz имеет Равный "класс типов", который решает эту проблему, хотя и с другим оператором.

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
2 голосов
/ 09 марта 2011

Если вы счастливы изменить название метода с == на другое, мы можем сделать это:

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
2 голосов
/ 09 марта 2011

К сожалению, вы не можете проверить это статически ... По крайней мере, не используя ==, который использует метод Java Object#equals, где все решительно определяется в терминах необработанных объектов.

Если вам нужна безопасность типов, тогда ваш единственный выбор - реализовать другой оператор, возможно, что-то вроде =|=, а затем объединить его с классами типов для обеспечения вашей безопасности.

Я полагаю, что у scalaz также есть что-то полезное для равенства типов, но я не знаю библиотеку достаточно хорошо, чтобы утверждать это наверняка.

Другой подход, который вы можете использовать, который будет безопасен только во время выполнения, - это использование canEqual шаблона , как описано здесь . Это уже используется case-классами и предлагает хороший способ выборочно нарушать LSP, когда это подходит для равенства.

1 голос
/ 09 марта 2011

Попробуйте:

 trait Fruit {
   val color: String
   def == (fruit: Fruit) = getClass == fruit.getClass && color == fruit.color
 }
...