How to define immutable objects in Java

In my code, I create a collection of objects that various threads will access in such a way that it is safe if the objects are immutable. When an attempt is made to insert a new object into my collection, I want to check whether it is immutable (if not, I will make an exception).

One thing I can do is check out a few known immutable types:

private static final Set<Class> knownImmutables = new HashSet<Class>(Arrays.asList( String.class, Byte.class, Short.class, Integer.class, Long.class, Float.class, Double.class, Boolean.class, BigInteger.class, BigDecimal.class )); ... public static boolean isImmutable(Object o) { return knownImmutables.contains(o.getClass()); } 

This actually gets me 90%, but sometimes my users want to create simple immutable types:

 public class ImmutableRectangle { private final int width; private final int height; public ImmutableRectangle(int width, int height) { this.width = width; this.height = height; } public int getWidth() { return width; } public int getHeight() { return height; } } 

Is there some way (possibly using reflection) that I can reliably determine if the class is immutable? False positives (considering it unchanged when it is not) are unacceptable, but false negatives (considering it volatile when it is not).

Edited to add: Thanks for the insightful and helpful answers. As indicated in some of the answers, I forgot to define my security goals. The threat here is ignorant developers - this is part of the framework code that will be used by a large number of people who know about the stream, know nothing and will not read the documentation. I do not need to defend myself against malicious developers - anyone smart enough to mutate a string or perform other frauds will also be smart enough to know this is not safe in this case. Static analysis of the code base is an option if it is automated, but code reviews cannot be taken into account because there is no guarantee that each review will have overlapping reviewers.

+41
java immutability functional-programming
Oct 15 '08 at 1:55
source share
15 answers

There is no reliable way to determine if a class is immutable. This is because there are so many ways that a class property can be changed, and you cannot detect them all through reflection.

The only way to get closer to this is:

  • Allow the final properties of immutable types (primitive types and classes, which, as you know, are immutable),
  • Require class to be final.
  • Require them to inherit from the base class that you provide (which is guaranteed to be immutable)

Then you can check with the following code if the object you have is immutable:

 static boolean isImmutable(Object obj) { Class<?> objClass = obj.getClass(); // Class of the object must be a direct child class of the required class Class<?> superClass = objClass.getSuperclass(); if (!Immutable.class.equals(superClass)) { return false; } // Class must be final if (!Modifier.isFinal(objClass.getModifiers())) { return false; } // Check all fields defined in the class for type and if they are final Field[] objFields = objClass.getDeclaredFields(); for (int i = 0; i < objFields.length; i++) { if (!Modifier.isFinal(objFields[i].getModifiers()) || !isValidFieldType(objFields[i].getType())) { return false; } } // Lets hope we didn't forget something return true; } static boolean isValidFieldType(Class<?> type) { // Check for all allowed property types... return type.isPrimitive() || String.class.equals(type); } 

Update:. As suggested in the comments, it could be extended to re-execute the superclass, rather than validating a specific class. It has also been suggested to recursively use isImmutable in the isValidFieldType method. Maybe this will work, and I also checked some tests. But this is not trivial. You cannot just check all field types with isImmutable call because String has not completed this test (its hash field is not final!). In addition, you can easily run infinite recursions, causing StackOverflowErrors;) Other problems can be caused by generics, where you also need to check their types for immunity.

I think that with some work, these potential problems can be solved in some way. But then you must first ask yourself if it is really worth it (also the ability to work).

+29
Oct. 15 '08 at 2:52
source share
— -

Use the Immutable annotation from Java Concurrency in practice . The FindBugs tool can help detect classes that change, but should not be.

+28
Oct. 15 '08 at 8:24
source share

At my company, we defined the @Immutable attribute. If you decide to attach this to a class, it means that you promise that you are immutable.

It works for documentation, and in your case it will work as a filter.

Of course, you still depend on the author keeping his word about immutability, but since the author has clearly added an annotation, this is a reasonable assumption.

+9
Oct 15 '08 at 2:14
source share

In principle, no.

You can create a giant whitelist of accepted classes, but I think a less crazy way would be to simply write in the documentation for the collection that all that happens is the collection must be immutable.

Edit: Other people suggested having an unchanged annotation. This is good, but you also need documentation. Otherwise, people will just think: “If I put this annotation in my class, I would save it in the collection,” and it just pulls it into anything, immutable and mutable classes. In fact, I would be afraid to have an invariable annotation just in case people think that an annotation makes their class unchanged.

+8
Oct. 15 '08 at 2:12
source share

This may be another hint:

If the class does not have setters, it cannot be mutated, provided by the parameters that it was created, are either "primitive" types, or they do not change themselves.

In addition, no methods can be overridden, all fields are final and private,

I'll try to do something for you tomorrow, but Simon's code using reflection looks pretty good.

At the same time, try to get a copy of Josh Blok’s book Effective Java, she has a Subject related to this topic. While there is no certainty about how to define a class that can be called, it shows how to create a good one.

The element is called: "Maintain immutability."

link: http://java.sun.com/docs/books/effective/

+4
Oct 15 '08 at 3:34
source share

In my code, I create a collection of objects to which various threads will be available, which are safe only if the objects are immutable.

This is not a direct answer to your question, but keep in mind that objects that are immutable are not automatically guaranteed by thread safety (unfortunately). The code must be free from side effects in order to be thread safe, and it is quite complicated.

Suppose you have this class:

 class Foo { final String x; final Integer y; ... public bar() { Singleton.getInstance().foolAround(); } } 

Then the foolAround() method may include some non-thread operations that will blow your application up. And this cannot be verified by reflection, since the actual link can only be found in the body of the method, and not in the fields or the public interface.

In addition, others are true: you can scan all the declared fields of the class, check whether each of them is final, as well as an immutable class, and you're done. I do not think that final tricks are a requirement.

Also, be careful when recursively checking dependent fields for immutability, you can get circles:

 class A { final B b; // might be immutable... } class B { final A a; // same so here. } 

Classes A and B are completely immutable (and perhaps even useful through some reflective hacks), but a naive recursive code will go into an endless loop of checking A, then B, then A again, then to B, ...

You can fix this with a “visible” card that forbids loops, or with some really smart code that decides that classes are immutable if all their dependencies are immutable only depending on themselves, but it will be really difficult. ..

+4
Oct 15 '08 at 7:43
source share

You can ask your clients to add metadata (annotations) and check them at runtime with reflection, for example:

Metadata:

 @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.CLASS) public @interface Immutable{ } 

Client code:

 @Immutable public class ImmutableRectangle { private final int width; private final int height; public ImmutableRectangle(int width, int height) { this.width = width; this.height = height; } public int getWidth() { return width; } public int getHeight() { return height; } } 

Then, using reflection in the class, check if it has an annotation (I would insert code, but its template could easily be found on the Internet)

+3
Oct. 15 '08 at 2:15
source share

why do all recommendations require the class to be final? if you use reflection to check the class of each object, and you can programmatically determine that this class is immutable (immutable, final fields), then you do not need to require the class itself to be final.

+3
15 Oct '08 at 16:31
source share

You can use AOP and @Immutable annotation from jcabi aspects :

 @Immutable public class Foo { private String data; } // this line will throw a runtime exception since class Foo // is actually mutable, despite the annotation Object object = new Foo(); 
+3
Feb 18 '13 at 7:02
source share

Like other defendants, he already said: IMHO there is no reliable way to find out if an object is really immutable.

I would just introduce the "Immutable" interface to check it when adding. This works as a hint that for some reason you should only insert immutable objects.

 interface Immutable {} class MyImmutable implements Immutable{...} public void add(Object o) { if (!(o instanceof Immutable) && !checkIsImmutableBasePrimitive(o)) throw new IllegalArgumentException("o is not immutable!"); ... } 
+2
Oct 15 '08 at 5:25
source share

Try the following:

 public static boolean isImmutable(Object object){ if (object instanceof Number) { // Numbers are immutable if (object instanceof AtomicInteger) { // AtomicIntegers are mutable } else if (object instanceof AtomicLong) { // AtomLongs are mutable } else { return true; } } else if (object instanceof String) { // Strings are immutable return true; } else if (object instanceof Character) { // Characters are immutable return true; } else if (object instanceof Class) { // Classes are immutable return true; } Class<?> objClass = object.getClass(); // Class must be final if (!Modifier.isFinal(objClass.getModifiers())) { return false; } // Check all fields defined in the class for type and if they are final Field[] objFields = objClass.getDeclaredFields(); for (int i = 0; i < objFields.length; i++) { if (!Modifier.isFinal(objFields[i].getModifiers()) || !isImmutable(objFields[i].getType())) { return false; } } // Lets hope we didn't forget something return true; } 
+2
Jul 27 '11 at 11:08
source share

Check out joe-e , an implementation of features for java.

0
Mar 04 '09 at 16:27
source share

Something that works for a high percentage of built-in classes is a test for instanceof Comparable. For classes that are not immutable, like Date, in most cases they are often considered immutable.

0
Mar 04 '09 at 19:21
source share

As far as I know, there is no way to identify immutable objects that are 100% correct. However, I wrote a library to bring you closer. It performs class bytecode analysis to determine if it is immutable or not, and can be executed at runtime. It is on the strict side, so it also allows you to use a white list of known immutable classes.

You can check it out: www.mutabilitydetector.org

It allows you to write code like this in an application:

 /* * Request an analysis of the runtime class, to discover if this * instance will be immutable or not. */ AnalysisResult result = analysisSession.resultFor(dottedClassName); if (result.isImmutable.equals(IMMUTABLE)) { /* * rest safe in the knowledge the class is * immutable, share across threads with joyful abandon */ } else if (result.isImmutable.equals(NOT_IMMUTABLE)) { /* * be careful here: make defensive copies, * don't publish the reference, * read Java Concurrency In Practice right away! */ } 

It is free and open source under the Apache 2.0 license.

0
May 30 '14 at 13:01
source share

I appreciate and admire the amount of work that Grundlefleck has contributed to his detection detector, but I think it's a bit overkill. You can write a simple, but almost very adequate (i.e., Pragmatic) detector as follows:

(note: this is a copy of my comment here: stack overflow

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.

0
Jan 23 '15 at 14:35
source share



All Articles