在混合的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?
答案 0 :(得分:1)
编辑:首先,您不应该在方法enforceEqualsHashImpl
中声明类型参数,因为它们会影响您在{1}}和KeyType
中声明的参数我觉得这不是你的意图。因此,您的方法的声明应该是:
ValueType
此外,内部方法中的类型参数def enforceEqualsHashImpl {
...
}
对我来说似乎也有点多余。
现在关于T
s:
如果您希望Scala编译器为类型参数提供ClassTag
,则必须在某个时刻指定具体的类型参数。这是因为有关类型参数的信息仅在编译时可用(类型擦除)。
如果您希望自己的示例有效,则需要将ClassTag
传递给您的班级:
ClassTag
否则,您的方法class SomeClass[KeyType : ClassTag, ValueType : ClassTag]
无法知道实际类型隐藏在enforceEqualsHashImpl
和KeyType
后面。
当然,在此更改之后,由于您的类需要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])
构建时有KeyType
和ValueType
的运行时类,你可以这样做:
SomeClass
这当然大致相当于传递def constructSomeClass[KeyType,ValueType](keyClass: Class[KeyType], valueClass: Class[ValueType]) =
new SomeClass[KeyType,ValueType](keyConv, valueConv)(ClassTag(keyClass), ClassTag(valueClass))
个对象本身而不是Class
s。