0

在混合 Java/Scala 环境中,我有一个参数化类,它需要其类型参数才能正确覆盖 equals 和 hashCode。感谢这个博客,我编写了这个方法来检查是否是这种情况,使用 ClassTag:

 class SomeClass[KeyType, ValueType] (arg1: Converter[KeyType], arg2:Converter[ValueType]) {
 // concrete values of KeyType/ValueType not available at construction time

 enforceEqualsHashImpl
 // enforceEqualsHashImpl[KeyType, ValueType] : "error: No ClassTag available for KeyType "

 def enforceEqualsHashImpl[KeyType : ClassTag, ValueType : ClassTag]= {

  def checkEqualsEqualsHash[T](c:Class[T]): Boolean = {

    def checkequals[T](c:Class[T]):Class[_] ={
      val equalsDeclaringClass = c.getMethod( "equals", classOf[Object]).getDeclaringClass();
      if ( classOf[Object].equals( equalsDeclaringClass ) )
        throw new Exception( "HazelIndex7 parametrized by key class" + c + " which does not override equals")
      equalsDeclaringClass
    }

    def checkHash[T](c:Class[T]):Class[_] ={
      val hashCodeDeclaringClass = c.getMethod( "hashCode").getDeclaringClass();
      if ( classOf[Object].equals( hashCodeDeclaringClass ) )
        throw new Exception( "HazelIndex7 parametrized by key class" + c + " which does not override hashCode")
      hashCodeDeclaringClass
    }

    val equalsDeclaringClass = checkequals(c)
    val hashDeclaringClass = checkHash(c)
    val equals = equalsDeclaringClass.equals(hashDeclaringClass)
    if (!equals)
      throw new Exception("Equals in class " + c + " is implemented by "+ equalsDeclaringClass + " but hashCode is implemented by: " + hashDeclaringClass );
    equals
  }

  checkEqualsEqualsHash(classTag[KeyType].runtimeClass)
  checkEqualsEqualsHash(classTag[ValueType].runtimeClass)
}

但是,我得到了这个例外:

 java.lang.NoSuchMethodException:     scala.runtime.Nothing$.equals(scala.runtime.Nothing$)
at java.lang.Class.getMethod(Class.java:1624)

在我看来,由于某种原因,ClassTag.runtimeClass 无法解析类型参数?这段代码有问题吗?什么可能导致此异常?

还尝试过这样的事情:

enforceEqualsHashImpl[KeyType, ValueType](classTag[KeyType], classTag[ValueType])
def enforceEqualsHashImpl[KeyType , ValueType ](implicit kc:ClassTag[KeyType],vc:ClassTag[ValueType])= ...

这导致:

error: No ClassTag available for KeyType
enforceEqualsHashImpl[KeyType, ValueType](classTag[KeyType], classTag[ValueType])    

error: No ClassTag available for ValueType ....

可能相关:请注意,类型参数必须由构造函数参数 (arg1/arg2) 间接推断,这些参数不是类型参数的具体值。

有没有更好的方法来检查equals/hashCode?

4

1 回答 1

1

编辑:首先,您不应该在 method 中声明类型参数enforceEqualsHashImpl,因为它们会影响您的类中声明KeyTypeValueType参数,我想这不是您的意图。所以你的方法的声明应该是:

def enforceEqualsHashImpl {
  ...
}

此外,您的内部方法中的类型参数T对我来说似乎也有点多余。

现在关于ClassTags:

如果您希望 Scala 编译器ClassTag为类型参数提供 s,则必须在某些时候指定具体的类型参数。这是因为有关类型参数的信息仅在编译时可用(类型擦除)。

如果您希望您的示例起作用,则需要将ClassTags 传递给您的班级:

class SomeClass[KeyType : ClassTag, ValueType : ClassTag]

否则,您的方法 ,enforceEqualsHashImpl不可能知道隐藏在KeyTypeand后面的实际类型ValueType

当然,在此更改之后,由于您的类需要ClassTags 作为其类型参数,因此您必须以两种方式之一构造它:

  • 通过传递具体类型参数(例如new SomeClass[String,Integer](keyConv, valueConv)
  • ClassTag在s 已经可用的地方做它,例如在这样的方法中:

    def constructSomeClass[KeyType: ClassTag, ValueType: ClassTag] = 
      new SomeClass[KeyType,ValueType](keyConv, valueConv)
    

    当然,在这种情况下constructSomeClass也必须使用具体的类型参数或某处ClassTags已经可用的地方调用,等等......

因此,总结一下 -如果您希望s 自动用于您的类型,则必须在某个级别指定具体的类型参数。ClassTag

当然你也可以ClassTag从像这样的对象手动创建 s Class

val classTag = ClassTag(classOf[String])

所以我想如果你有运行时类KeyType并且ValueType在构建时可用SomeClass,你可以做这样的事情:

def constructSomeClass[KeyType,ValueType](keyClass: Class[KeyType], valueClass: Class[ValueType]) = 
  new SomeClass[KeyType,ValueType](keyConv, valueConv)(ClassTag(keyClass), ClassTag(valueClass))

这当然大致相当于传递Class对象本身而不是ClassTags。

于 2013-03-02T13:20:42.950 回答