考虑以下:

Map<Class<?>, Object> myMap = new HashMap<Class<?>, Object>();
Foo fooObject = New Foo();
myMap.put(fooObject.getClass(), fooObject)

请注意,java.lang.Class并没有实现hashCode()方法本身,而是隐式地从java.lang.Object继承了它。我在JDK 1.8中对此进行了验证。
java.lang.Class可以安全用作java.util.HashMap的 key 吗?myMap.get(Foo.class)是否总是像myMap.put(fooObject.getClass(), fooObject)一样返回我输入的值?考虑该软件具有各种类加载器和序列化机制。结果会还是一样吗?如果没有,那有什么选择呢?

最佳答案



是的。



是的。

Class对象用作HashMap中的键是安全的。 Class类继承了Object::equalsObject::hashCode方法。因此,equals对象的Class正在测试对象身份。

这是Java中类型相等的正确语义。 ClassLoader::defineClass方法的实现可确保您永远无法获得表示相同Java类型的两个不同的Class对象。

但是,有皱纹。 Java语言规范(JLS 4.3.4)声明如下:



(二进制名称与命名类型的FQDN相关,并考虑了匿名类和数组类型。)

这意味着如果(成功)在两个不同的类加载器中为具有完全限定名称的类调用ClassLoader::defineClass,您将获得不同的Java类型。与您使用的字节码无关。此外,如果尝试从一种类型转换为另一种类型,则将获得类转换异常。

现在的问题是,这在您的用例中是否重要?

答:可能不会。

  • 除非您(或您的框架)使用类加载器进行棘手的事情,否则不会出现这种情况。
  • 如果这样做,那么您可能需要两种类型(具有相同的FQDN和不同的类加载器)才能在HashMap中具有不同的条目。 (因为类型不同!)
  • 但是,如果您需要两种类型具有相同的条目,则可以使用该类的FQDN作为键,可以使用Class::getCanonicalName获得该键。如果需要处理数组类等,请使用Class::getName,它返回该类型的二进制名称。


  • 序列化机制又如何呢?
    Class对象无法使用对象序列化进行序列化,因为Class不实现Serializable。如果实现/使用其他确实支持Class对象序列化的序列化机制,则该机制需要与JLS 4.3.4兼容。

    09-30 15:17
    查看更多