类实例是不可变的吗?

时间:2018-08-29 16:26:08

标签: java reflection set immutability

我想知道Class实例是否不可变。声明的方法名称并不意味着实例状态在调用时会更改,但是我在javadoc上没有明确的保证。

场景:我需要在Set中存储唯一的类名。理想情况下,当我需要通过反射访问类时,我想用Class实例填充集合,以避免不必要地调用Class.forName()。但是,最好使用不可变的对象作为集合的键。因此,我想知道是否可以立即使用Class实例。

2 个答案:

答案 0 :(得分:4)

首先,泛型部分Class<?>在这里确实无关紧要。当然,没有原始类型,因此Class<?>比Class更好,但是对于您的问题,通配符并不重要。

因此,从本质上讲,您在问Class对象是否不可变。出于所有实际目的,它们是。

当类加载器加载一个类时,类对象就存在了,除非整个类加载器及其所有加载都被卸载,否则它们将保持放置状态。

当此类类对象仍在地图中的某处使用时,不会发生这种情况。

另一方面:Class.forName()对于已经加载的类来说应该不会太昂贵。并且当诸如序列化之类的事情开始起作用时,人们建议例如使用String而不是Class对象(请参见here)。

必须区分类对象的不变 identity 和属于该类的实际“代码”。可以在运行时更改代码 (通过检测,可以考虑代码的热插拔)。但是,类名,其每个代码以及equals()的相等性均不受此影响。因为“身份”保持不变。

最后的注释:正如下面有趣的注释所述,有一定的方法可以在一定程度上更改 Class对象。但是所有这些活动绝对是“超出正常范围”的。因此:从理论上讲,您可能更喜欢Strings而不是Class对象,但是实际上,在“普通”应用程序中,使用Class应该也可以。

答案 1 :(得分:0)

由于我不同意其他答案,因此我决定写这个答案,
类不是一成不变的,但它们是唯一的-一个类只能存在一个Class对象实例。

但它不是由其名称定义的类,因为类可能来自不同的类加载器,并且不同的类加载器可能具有名称相同的类-但这将是不同的类,如果您会得到ClassCastException如果两个对象都存在相同的对象类型(作为一个单独的对象,而不是继承的),则在两个不同的类加载器处理的代码之间传递一些对象。

类实例仍可以在Set中安全地使用,因为它们使用哈希集/等于的默认实现,因此仅将Class的相同实例视为相等。

但是要确定您应该使用String还是Class,您需要知道您的应用应该如何工作,就像我说的那样,不同类加载器之间可以存在多个同名类。

通过仅存储类名,您不能确定Class.forName将返回预期的相同实例,甚至可能从当前的类加载器中加载其他具有相同名称的类,而不是使用预期的类。