13

Before concretizing my question, let me provide some background: My main programming languages are C++ and Java. When working with C++ I find it important to apply const correctness, that is declaring functions like this:

A::DoSomething( const B& arg ); // guarantees that arg is not modified
A::DoSomething() const; // guarantees that the object of type A is not modified
A::DoSomething( const B& arg ) const; // both of the above

In fact, I often wish that const would be the default and objects that are modified have to be marked somehow.

My main reasons for using constare:

  • Communication with other developers: It makes code more expressive.
  • Communication with the compiler: It helps to find issues at compile time and sometimes makes additional optimizations possible.

It is well-known that Java has no const keyword (you can't do the above with final) and this fact has been discussed here before, see for example here: Equivalent of const(C++) in Java.

The Java alternative that is usually proposed is to make your classes immutable. While this is not a full replacement of const because it works per class and not per context in which a class is used, it would work fine for me in most cases.

But there is one big issue with immutable classes: Immutability is not obvious. To find out if a class really is immutable, as far as I know, you basically have to check the full source code. Any method could have a backdoor through which the object could be modified.

So is there an easier way to check immutability? Or is there any best practice to somehow mark a class as being immutable?

Note: I know that both languages provide 'evil tricks' to bypass constness or immutability: C++ has const_cast and Java has reflection. But for the context of my question, let's assume that these are not used.

Community
  • 1
  • 1
Frank Puffer
  • 7,945
  • 2
  • 18
  • 42
  • 2
    Fun fact: Java does have [a const keyword](https://docs.oracle.com/javase/tutorial/java/nutsandbolts/_keywords.html), but it is not used ;) – Jorn Vernee May 07 '16 at 11:20
  • One big hint to a class being immutable to me (or having that intent), is if it were marked ```final```. – Jorn Vernee May 07 '16 at 11:35
  • Very much related: [this Q/A which goes into detection of immutability for runtime code](http://stackoverflow.com/questions/203475/how-do-i-identify-immutable-objects-in-java) (if it wasn't I would have closed this Q down as a dupe). – Maarten Bodewes Feb 14 '17 at 15:04

2 Answers2

18

Java has no first-class immutability support, so you have no reliable way to detect if class is immutable.

Java Concurrency In Practice recommends (see Appendix A as reference) to use class-level @Immutable annotation from javax.annotation.concurrent and this is the most convenient, common and standard way to indicate immutability; custom javadoc is fine too. Note that it's only declaration, not actual constraint.

Design of the class is also a good indicator:

  • Have only final fields (but in rare cases it's possible to have some non-final fields and still be immutable, for example see String#hashCode)
  • It's properly constructed (this reference doesn't leak from constructor)
  • Object state cannot be modified (so class should not have setters and mutator methods)
  • Don't store external (passed to constructor) references to mutable objects (e.g. create defensive copy of passed collection argument)

Full list of immutable class design properties can be found in Oracle tutorials.

So, to check if class is immutable you first look at class level annotations and javadoc and only then at implementation itself.

To provide additional sanity-check (if you think that annotated as immutable class can be mutable by mistake), there is Mutability Detector plugin for FindBugs (static analysis tool), which effectively doing same thing listed above: checks class has @Immutable annotation and verifies (via reflection) that all immutability rules are satisfied (with some additional things like immutable collection support from Guava etc.). Mutability detector also can be used as library without FindBugs, which allows you to write tests like that:

@Test
public void testImmutable() {
    assertImmutable(MyClass.class);
}
qwwdfsad
  • 2,919
  • 15
  • 24
  • 1
    Im getting compiler error with @Immutable annotation in my code - `cannot find symbol @Immutable symbol: class Immutable 1 error` – roottraveller Jul 04 '17 at 10:33
  • @roottraveller You can find the `@Immutable` annotation in JSR305: ` com.google.code.findbugs jsr305 3.0.2 ` – Julien Kronegg Jun 15 '18 at 21:14
  • Regarding `final` fields: this only give the guaranty that the _reference_ will not be changed. However, the _value_ of a mutable field (e.g. `java.util.List`) can still be changed. – Julien Kronegg Jun 15 '18 at 21:18
  • Java now has [`record`](https://openjdk.java.net/jeps/384). Alas, it does not (yet?) declare `@Immutable`. – Aleksandr Dubinsky Feb 23 '21 at 09:15
-2

According to Java documentation:

  • Don't provide "setter" methods — methods that modify fields or objects referred to by fields.
  • Make all fields final and private.
  • Don't allow subclasses to override methods. The simplest way to do this is to declare the class as final. A more sophisticated approach is to make the constructor private and construct instances in factory methods.
  • If the instance fields include references to mutable objects, don't allow those objects to be changed:
    • Don't provide methods that modify the mutable objects.
    • Don't share references to the mutable objects. Never store references to external, mutable objects passed to the constructor; if necessary, create copies, and store references to the copies. Similarly, create copies of your internal mutable objects when necessary to avoid returning the originals in your methods.

The easiest way to enforce immutability is via @Value annotation in Lombok library.

If you use IntelliJ, you can check class byte code for above-mentioned bullets enter image description here

max3d
  • 1,265
  • 14
  • 16
  • 1
    This is not a good example because the Lombok `@Value` only helps you to have an immutable class, but the Lombok implementation is incomplete (at least at the time of writing). In your case, the `java.util.Date` field is still mutable even if `private final`: the Lombok generated constructor does not clone the Date, so it can be modified externally, e.g. `Date d = new Date(); FinalClass fc = new FinalClass(1,"",d); d.setTime(0);`. See also in https://projectlombok.org/features/Value : the `String[]` argument is not cloned in the constructor, so its content can be modified afterwards. – Julien Kronegg Jun 15 '18 at 21:31
  • Setting a field as `final` is not a guarantee to make it immutable. This is only the _reference_ that is immutable, not its _value_ (see your `java.util.Date` field). The reverse is also true: your field may be immutable even if not `final` (see the cached value of the `hashCode()` method from the immutable class `String`). – Julien Kronegg Jun 15 '18 at 21:39
  • This does not answer to the OP question (_how to find out if a class is immutable?_). – Julien Kronegg Jun 15 '18 at 21:49