我希望有几个数据类都具有指定的标识符
足以检查对象的相同性。
但是这个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])
注释时会产生问题{1}}(将true
替换为Manifest
)。
那么,为什么I == J
是{{1}}?由于类型擦除?有可能“修复”这个{{1}}吗?或者是否有另一种可能性来确保{{1}}?
答案 0 :(得分:2)
确实是类型擦除。你必须在case that: Ident[I]
上收到一些警告。它检查你有一个Ident[somehing]
但不是这个东西是I
(与java中的相同)。
最好与that: Ident[_]
一起检查您没有错误保证。
您的课程中可能确实有Manifest
,并检查清单是否相等。
class Ident[I <% Ordered](val id: I)(implicit val manifest: Manifest[I])
def canEqual(that: Any) = that match {
case Ident[_] if this.manifest == that.manifest => ...
case _ => false
}
}
我建议I : Ordering
而不是I % Ordered
(一般来说,不只是针对您的问题)。然后,您可以比较排序而不是清单。你也应该使用它在id上做相同的,这样你知道它与排序
修改关于您的评论。
我认为这具有相同的Ordering
就足以说canEqual
是true
。然后,编译器知道的this.Odering
是Ordering[I]
,而that.Ordering
是编译器不知道的类型的排序。你将不得不使用第一个,这将暗示对.I的未经检查的强制转换(模式匹配或其他)。但是,检查了顺序相等应该足以确保强制转换是安全的(Ordering
是不变的)
答案 1 :(得分:0)
关于didierd的回答是尝试使用implicit ord: Ordering[I]
替换必要的 Manifest
以确保类型兼容性。这工作正常,它出现如下。
abstract class Ident[I](val id: I, dom: String = "")(implicit val ord: Ordering[I])
extends Equals with Ordered[Ident[I]]
{
protected val domain: AnyRef = if (dom != "") dom else ord
override lazy val hashCode = id.##
def canEqual(other: Any) = other match {
case that: Ident[_] => this.domain eq that.domain
case _ => false
}
override def equals(other: Any) = other match {
/* ugly: non variable type-argument I in type pattern Ident[I]
is unchecked since it is eliminated by erasure */
case that: Ident[I] if this.domain eq that.domain
=> (that canEqual this) && ord.compare(this.id, that.id) == 0
case _ => false
}
def compare(that: Ident[I]): Int = if (that canEqual this)
ord compare (this.id, that.id)
else
{
val message = "'%s' and '%s' are not comparable!" format (this, that)
throw new IllegalArgumentException(message)
}
lazy val nameId: String = getClass.getSimpleName + "#" + id
override def toString = nameId
}
可以创建域(子类层次结构),其元素可以相互比较
/* Order, SubOrder and all other <: Ident[Int] without explicit dom are comparable */
class Order(i: Int) extends Ident[Int](i)
class SubOrder(i: Int) extends Order(i)
/* Node and SubNode's are in its own "Node" named domain
and comparable only among each other */
class Node(i: Int) extends Ident[Int](i, "Node")
class SubNode(i: Int) extends Node(i)
没有为每个域实现单独的canEqual
样板。
只剩下一件事:丑陋警告Ident的equals
方法。