Я хочу иметь несколько классов данных, каждый из которых имеет идентификатор, назначенный достаточным для проверки равенства объектов.Но это id: I
следует использовать не только для проверки равенства, но и для сравнения.Вот почему граница представления I <% Ordered[I]
заявлена в следующем базовом классе.
abstract class Ident[I <% Ordered[I]](val id: I)
extends Equals with Ordered[Ident[I]]
{
override lazy val hashCode = id.hashCode
/* canEqual does not work as desired! */
def canEqual(other: Any) = other match {
case that: Ident[I] => true /* id < that.id || id > that.id || id == that.id */
case _ => false
}
override def equals(other: Any) = other match {
case that: Ident[I] => (that canEqual this) && this.id == that.id
case _ => false
}
def _compare(that: Ident[I]): Int = {
if (that canEqual this) this.id compare that.id else {
val message = "'%s' and '%s' are not comparable!" format (this, that)
throw new IllegalArgumentException(message)
}
}
def compare(that: Ident[I]): Int = _compare(that)
}
Метод compare
определяется только тогда, когда canEqual
равен true
.
Поскольку производные классыT <: Ident[I]
тоже должно быть Ordered[T]
, неявное преобразование определено:
object Ident {
implicit def ident2ordered[I, T <: Ident[I]](other: T): Ordered[T] = {
new Ordered[T] {
def compare(that: T): Int = other._compare(that)
}
}
}
А вот некоторые классы производных данных:
/* should be comparable with all other <: Ident[Int] */
class IntId(i: Int) extends Ident[Int](i)
/* should be comparable only with itself */
class YetAnotherIntId(y: Int) extends Ident[Int](y) {
override def canEqual(other: Any) = other.isInstanceOf[YetAnotherIntId]
override def equals(other: Any) = other match {
case that: YetAnotherIntId => super.equals(that)
case _ => false
}
}
/* should be comparable with all other <: Ident[Long] */
class LongId(j: Long) extends Ident[Long](j)
/* should be comparable with all other <: Ident[String] */
class StringId(s: String) extends Ident[String](s)
А теперь заметил (но не всегда)желаемое) поведение:
val i12 = new IntId(12)
val i13 = new IntId(13)
i12 canEqual i13 /* => true */
i12 < i13 /* => true */
val y12 = new YetAnotherIntId(12)
val y13 = new YetAnotherIntId(13)
y12 canEqual y13 /* => true */
y12 < y13 /* => true */
i12 canEqual y12 /* => true */
y12 canEqual i12 /* => false */
i12 == y12 /* => false */
y12 == i12 /* => false */
val j12 = new LongId(12L)
val j13 = new LongId(13L)
j12 canEqual j13 /* => true */
j12 < j13 /* => true */
i12 canEqual j12 /* => true but want false because Int != Long */
j12 canEqual i12 /* => true '' */
i12 == j12 /* => true '' */
j12 == i12 /* => true '' */
val s12 = new StringId("12")
val s13 = new StringId("13")
s12 canEqual s13 /* => true */
s12 < s13 /* => true */
i12 canEqual s12 /* => true but want false because Int != String */
s12 canEqual i12 /* => true '' */
i12 == s12 /* => false */
s12 == i12 /* => false */
Спасибо, если вы прочитали это далеко, но теперь вопросы:
Как мне добиться, чтобы Ident[I].canEqual(Ident[J])
было false
для I != J
без переопределения canEqual
как в YetAnotherIntId
?
Кажется, что Ident[I]
это Ident[J]
это Ident[_]
, что может привести к проблемам при использовании this.id
и that.id
вместекомментируя в Ident::canEqual
(заменив true
на this.id < that.id || this.id > that.id || this.id == that.id
).
Итак, почему Ident[Int].canEqual(Ident[Long])
равно true
?Из-за стирания типа?Можно ли "починить" это с Manifest
?Или есть другая возможность убедиться, что I == J
?