Equals和Ordered [T]的继承(类层次结构)(类型参数擦除)

时间:2011-09-06 08:57:14

标签: scala pattern-matching type-erasure

我希望有几个数据类都具有指定的标识符 足以检查对象的相同性。 但是这个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方法仅在canEqualtrue时定义。

因为派生类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.idIdent::canEqual,当truethis.id < that.id || this.id > that.id || this.id == that.idIdent[Int].canEqual(Ident[Long])注释时会产生问题{1}}(将true替换为Manifest)。

那么,为什么I == J是{{1}}?由于类型擦除?有可能“修复”这个{{1}}吗?或者是否有另一种可能性来确保{{1}}?

2 个答案:

答案 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就足以说canEqualtrue。然后,编译器知道的this.OderingOrdering[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方法。