Must all properties of an immutable object be final?

The main difference between an immutable object (all properties final) and an effectively immutable object (properties aren't final but can't be changed) is safe publication.

You can safely publish an immutable object in a multi threaded context without having to worry about adding synchronization, thanks to the guarantees provided by the Java Memory Model for final fields:

final fields also allow programmers to implement thread-safe immutable objects without synchronization. A thread-safe immutable object is seen as immutable by all threads, even if a data race is used to pass references to the immutable object between threads. This can provide safety guarantees against misuse of an immutable class by incorrect or malicious code. final fields must be used correctly to provide a guarantee of immutability.

As a side note, it also enables to enforce immutability (if you try to mutate those fields in a future version of your class because you have forgotten it should be immutable, it won't compile).


Clarifications

  • Making all the fields of an object final does not make it immutable - you also need to make sure that (i) its state can't change (for example, if the object contains a final List, no mutating operations (add, remove...) must be done after construction) and (ii) you don't let this escape during construction
  • An effectively immutable object is thread safe once it has been safely published
  • Example of unsafe publication:

    class EffectivelyImmutable {
        static EffectivelyImmutable unsafe;
        private int i;
        public EffectivelyImmutable (int i) { this.i = i; }
        public int get() { return i; }
    }
    
    // in some thread
    EffectivelyImmutable.unsafe = new EffectivelyImmutable(1);
    
    //in some other thread
    if (EffectivelyImmutable.unsafe != null
        && EffectivelyImmutable.unsafe.get() != 1)
        System.out.println("What???");
    

    This program could in theory print What???. If i were final, that would not be a legal outcome.


Immutable = not changeable. So making properties final is a good idea. If not all properties of an object are protected from being changed I wouldn't say the object is immutable.

BUT an object is also immutable if it doesn't provide any setters for it's private properties.


Immutable objects MUST not be modified in any way after their creation. final of course helps to achieve that. You guarantee that they will not ever be changed. BUT what if you have an array inside your object that is final? Of course the reference is not changable, but the elements are. Look here at almost the same question I gave also:

Link


You can easily guarantee immutability by encapsulation alone, so it's not necessary:

// This is trivially immutable.
public class Foo {
    private String bar;
    public Foo(String bar) {
        this.bar = bar;
    }
    public String getBar() {
        return bar;
    }
}

However, you also must guarantee it by encapsulation in some cases, so it's not sufficient:

public class Womble {
    private final List<String> cabbages;
    public Womble(List<String> cabbages) {
        this.cabbages = cabbages;
    }
    public List<String> getCabbages() {
        return cabbages;
    }
}
// ...
Womble w = new Womble(...);
// This might count as mutation in your design. (Or it might not.)
w.getCabbages().add("cabbage"); 

It's not a bad idea to do so to catch some trivial errors, and to demonstrate your intent clearly, but "all fields are final" and "the class is immutable" are not equivalent statements.