执行TypeParameters的覆盖equals / hashCode。 classTag [T] .runtimeClass解析为Nothing / No ClassTag

时间:2013-03-02 12:02:31

标签: scala reflection equals hashcode

在混合的Java / Scala环境中,我有一个参数化类,需要其类型参数才能正确覆盖equals和hashCode。感谢this blog我编写了这个方法来检查是否是这种情况,使用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?

1 个答案:

答案 0 :(得分:1)

编辑:首先,您不应该在方法enforceEqualsHashImpl中声明类型参数,因为它们会影响您在{1}}和KeyType中声明的参数我觉得这不是你的意图。因此,您的方法的声明应该是:

ValueType

此外,内部方法中的类型参数def enforceEqualsHashImpl { ... } 对我来说似乎也有点多余。

现在关于T s:

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

如果您希望自己的示例有效,则需要将ClassTag传递给您的班级:

ClassTag

否则,您的方法class SomeClass[KeyType : ClassTag, ValueType : ClassTag] 无法知道实际类型隐藏在enforceEqualsHashImplKeyType后面。

当然,在此更改之后,由于您的类需要ValueType作为其类型参数,因此您必须使用以下两种方法之一构建它:

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

    ClassTag

    当然,在这种情况下,def constructSomeClass[KeyType: ClassTag, ValueType: ClassTag] = new SomeClass[KeyType,ValueType](keyConv, valueConv) 也必须使用具体的类型参数调用,或者某处,constructSomeClass已经可用,等等......

因此,总结一下 - 如果您希望自动为您的类型提供ClassTags必须在某个级别指定具体的类型参数。

当然,您也可以从ClassTag对象手动创建ClassTag,如下所示:

Class

所以我想如果你在val classTag = ClassTag(classOf[String]) 构建时有KeyTypeValueType的运行时类,你可以这样做:

SomeClass

这当然大致相当于传递def constructSomeClass[KeyType,ValueType](keyClass: Class[KeyType], valueClass: Class[ValueType]) = new SomeClass[KeyType,ValueType](keyConv, valueConv)(ClassTag(keyClass), ClassTag(valueClass)) 个对象本身而不是Class s。