我想确保给定的一组对象是不可变的。
我正在考虑以下几点:
所以我猜我的问题是:可能吗?
我可以递归地检查一个类的每个成员是否都有其字段private final
,但这还不够,因为一个类可以有一个名为getHaha(param)
的e方法,它将给定的param添加到一个数组中
那么有一种很好的方法来检查一个对象是不可变的还是可能的呢?
谢谢,
答案 0 :(得分:5)
您可能想要查看此项目:
该库试图分析特定类的字节码,以发现它是否是不可变的。它允许在单元测试中测试此条件,如可用的视频here所示。它肯定不完美(String字段将被视为可变,并且您的数组示例处理不当)但它比FindBugs提供的更复杂(即仅检查每个字段是否为最终字段)。
免责声明:我写了它; - )
答案 1 :(得分:3)
如果您生成数据模型及其所有代码,则可以确保您创建的可能的数据值对象不可变,以满足您的需求。
你遇到的问题是存在不同形式的不变性。即使String
也会导致您的测试失败Are String, Date, Method immutable?您可以通过这种方式证明某个类是完全不可变的,但您可能最好不要生成数据模型。
答案 2 :(得分:2)
是的,您可以编写不变性检测器。
首先,你不会只是编写一个方法来确定一个类是否是不可变的;相反,你需要编写一个不变性检测器类,因为它必须保持一些状态。检测器的状态将是迄今为止检测过的所有类的检测到的不变性。这不仅对性能有用,而且实际上是必要的,因为类可能包含循环引用,这会导致简化的不变性检测器陷入无限递归。
类的不变性有四个可能的值:Unknown
,Mutable
,Immutable
和Calculating
。您可能希望有一个地图,它将您目前遇到的每个类与不变性值相关联。当然,Unknown
实际上并不需要实现,因为它将是任何尚未出现在地图中的类的隐含状态。
因此,当您开始检查某个类时,会将其与地图中的Calculating
值相关联,完成后,将Calculating
替换为Immutable
或{{ 1}}。
对于每个班级,您只需要检查字段成员,而不是代码。检查字节码的想法是错误的。
首先,你应该不检查课程是否是最终的;一个阶级的终结性不会影响其不变性。相反,一个期望不可变参数的方法首先应该调用不变性检测器来断言传递的实际对象的类的不变性。如果参数的类型是最终类,则可以省略该测试,因此最终性有利于性能,但严格来说不是必需的。此外,正如您将进一步了解的那样,一个类型为非final类的字段将导致声明类被认为是可变的,但仍然是,这是一个声明类的问题,而不是问题。非最终不可变成员类。拥有一个不可变类的高层次结构是完全没有问题的,其中所有非叶子节点当然必须是非最终的。
你应该不检查某个字段是否为私有字段;对于一个拥有公共领域的类来说,这是完全正确的,并且该领域的可见性不会以任何方式,形状或形式影响声明类的不变性。您只需要检查该字段是否为final,其类型是不可变的。
在检查课程时,首先要做的是递归以确定其Mutable
课程的不变性。如果super是可变的,那么后代也是可变的。
然后,您只需要检查该类的声明的字段,而不是所有字段。
如果某个字段是非最终字段,则您的类是可变的。
如果某个字段是最终字段,但该字段的类型是可变的,那么您的类是可变的。 (根据定义,数组是可变的。)
如果某个字段是最终字段,并且该字段的类型为super
,则忽略该字段并继续执行下一个字段。如果所有字段都是不可变的或Calculating
,那么您的类是不可变的。
如果字段的类型是接口,抽象类或非最终类,那么它将被视为可变,因为您完全无法控制实际实现可能执行的操作。这似乎是一个不可逾越的问题,因为这意味着将一个可修改的集合包装在Calculating
内仍然会使不变性测试失败,但它实际上很好,并且可以通过以下解决方法来处理。
某些类可能包含非最终字段,但仍然实际上是不可变的。一个例子是UnmodifiableCollection
类。属于此类别的其他类是包含纯粹用于性能监视目的的非最终成员(调用计数器等)的类,实现 popsicle immutability (查找它)的类以及包含的类作为已知不会引起任何副作用的接口的成员。此外,如果一个类包含真正的可变字段但承诺在计算hashCode()和equals()时不考虑它们,那么当涉及到多线程时,该类当然是不安全的,但它仍然可以被认为是为了将它用作地图中的键而不可变。因此,所有这些案例都可以通过以下两种方式之一来处理:
手动将类(和接口)添加到不变性检测器。如果您知道某个类实际上是不可变的,尽管它的不变性测试失败,您可以手动向检测器添加一个条目,将其与String
相关联。通过这种方式,探测器永远不会尝试检查它是否是不可变的,它总是只是说'是的,它是。'
介绍Immutable
注释。您的不变性检测器可以检查字段上是否存在此注释,如果存在,它可以将该字段视为不可变字段,尽管字段可能是非最终字段或其类型可能是可变的。检测器还可以检查类上是否存在此注释,从而将类视为不可变,甚至无需检查其字段。
我希望这有助于后代。
答案 3 :(得分:1)
我怀疑你能用单元测试做到这一点。最好的方法是在编写课程或查看代码时要小心。正是因为问题,对象上的方法可以改变它可能从外部看不到的状态。仅仅因为它的沮丧并不意味着它不会发生: - )
答案 4 :(得分:1)
很确定这是不可能的。考虑这个功能:
public void doSomething() {
if (System.currentTimeMillis() % 100000 == 0) {
this.innerMember.changeState();
}
}
首先,您将无法通过运行每个类函数来检测它,因为此函数仅在100秒内精确地更改对象的状态。
其次,您将无法通过解析代码来检测它,因为您不知道changeState()
函数是否会更改innerMember的状态。
答案 5 :(得分:1)
此主题可以帮助How do I identify immutable objects in Java。看看第二个流行的答案,可能会检查FindBugs的任何不变性问题。如果您在每次提交时运行它,那么您可以将其称为单元测试:)
修改的
似乎FindBugs只检查final
,这并不多。您可以根据您在代码中使用的模式和类来实现自己的规则。