Unit testing for object immutability

I want a specific group of objects to be immutable.

I was thinking of something like:

  • check if each field is closed final
  • check if the class is final
  • check mutable items

So, I think, my question is: is it possible 3?

I can check recursively whether each class member has its own private final fields, but this is not enough, because the class can have an e-method called getHaha(param) , which adds this parameter to the array, for example.

So, is there a good way to check if an object is immutable or even possible?

Thank,

+9
java immutability unit-testing
Jul 19 '11 at 7:50
source share
6 answers

If you create your own data model and all its code, you can guarantee that the Data Value objects you create will be unchanged according to your needs.

The problem is that there are different forms of immutability. Even String will complete the test. Are String, Date, Method immutable? You can prove that the class is strictly indispensable in this way, but you will probably be better off generating your data model.

+2
Jul 19 '11 at 7:57
source share

You can check out this project:

Reciprocity meter

This library tries to parse the bytecode of a particular class to see if it is immutable or not. It allows you to test this condition in unit test, as shown in the video here . This, of course, is not ideal (the String field will be considered mutable, and the array example is not handled well), but it is more complex than what FindBugs offers (i.e. just checking that each field is final).

Disclaimer: I wrote this; -)

+5
Oct. 17 '11 at 11:45
source share

Yes, you can write a immutability detector.

First of all, you will not just write a method that determines whether a class is immutable; instead, you will need to write an immutability detection class because it will need to maintain some state. The state of the detector will be the detected immutability of all the classes that he has studied so far. This is not only useful for performance, but it is actually necessary, because the class may contain a circular reference, which will make the simplified immutability detector fall into infinite recursion.

The immutability of a class has four possible meanings: Unknown , Mutable , Immutable and Calculating . You probably want to have a map that links each class you come across to a value of immutability. Of course, Unknown does not actually need to be implemented, since this will be the implied state of any class that is not yet on the map.

So, when you start to study the class, you map it to the Calculating value on the map, and when you finish, you replace Calculating with Immutable or Mutable .

For each class, you only need to check the field members, not the code. The idea of ​​checking bytecode is pretty wrong.

First of all, you should not check if the class is final; The finiteness of a class does not affect its immutability. Instead, a method that expects an immutable parameter must first invoke the immutability detector to assert the immutability of the class of the actual object that was passed. You can skip this test if the parameter type is the final class, so finality is good for performance, but, strictly speaking, is not needed. In addition, as you will see later, a field whose type is of an odd class will cause the declaration class to be considered mutable, but nevertheless the problem is the declaring class, and not the problem of the unconfigured immutable class of participants. It is absolutely wonderful to have a high hierarchy of immutable classes, in which all non-leaf nodes must, of course, be non-final.

You must not check if the field is private; excellent if the class has a public field, and the visibility of the field does not affect the immutability of the declaring class in any way, form or form. You only need to check if the field is final and its type is immutable.

When learning a class, what you want to do first is recursion to determine the immutability of its super class. If super is mutable, then the descendant can also be changed by definition.

Then you only need to check the declared fields of the class, and not all the fields.

If the field is not final, your class is modified.

If the field is final, but the field type is mutable, then your class is changed. (Arrays are by definition mutable.)

If the field is final and the field type is Calculating , then ignore it and move on to the next field. If all fields are immutable or Calculating , then your class is immutable.

If the field type is an interface, or an abstract class, or a non-finite class, then it should be considered mutable, since you have absolutely no control over what the actual implementation can do. This may seem like an insurmountable problem, because it means that the wrapper of the modifiable collection inside the UnmodifiableCollection will still fail in the immutability test, but in fact this is normal and can be handled with the following workaround.

Some classes may contain non-final fields and still be effectively unchanged. An example of this is the String class. Other classes that fall into this category are classes that contain non-final elements exclusively for performance monitoring purposes (call counts, etc.), classes that implement popsicle immutability (look up), and classes that contain members that are Interfaces that are known to not cause any side effects. Also, if a class contains bona fide mutable fields, but promises do not take them into account when computing hashCode () and equals (), then the class is of course unsafe when it comes to multithreading, but it can still be considered immutable for the purpose of using it as a key on the map. Thus, all these cases can be handled in one of two ways:

  • Manually adding classes (and interfaces) to your immutability detector. If you know that a particular class is virtually immutable, even though the Immutable test fails for it, you can manually add an entry to your detector that binds it to Immutable . Thus, the detector will never try to check whether it is immutable, it will always just say "yes, it is."

  • Introducing the @ImmutabilityOverride annotation. Your immutability detective can check for the presence of this annotation on the field, and if present, he can consider the field as unchanged, despite the fact that the field may not be final or its type may be variable. The detector can also check for the presence of this annotation on the class, thereby treating the class as immutable, without even bothering to check its fields.

I hope this helps future generations.

+2
Jan 23 '15 at 13:36
source share

I doubt that you can do this with unit tests. The best way would be to be careful while writing a class or learning code. It is because of the problem that the methods on the object can mutate its state, which you do not see from the outside. Just because he is discouraged does not mean that this is not happening :-)

+1
Jul 19 2018-11-11T00:
source share

Pretty sure this is not possible. Consider this function:

 public void doSomething() { if (System.currentTimeMillis() % 100000 == 0) { this.innerMember.changeState(); } } 

Firstly, you will not be able to detect it by running each function of the class, since this function only changes the state of an object every 100 seconds.

Secondly, you will not be able to detect this by analyzing the code, since you do not know whether the changeState() function will change the state of innerMember or not.

+1
Jul 19 2018-11-11T00:
source share

This thread can help How to define immutable objects in Java . Take a look at the second popular answer, maybe you can check any immutability issues with FindBugs. If you run it on every commit, you can call it unit test :)

EDIT

It seems that FindBugs only checks for final , which is not much. You can implement your own rule according to your templates and classes that you use in your code.

+1
Jul 19 '11 at 8:00 a.m.
source share



All Articles