In my quest to make an object immutable in Java, I marked the class final, All of its variables final and provided no setters and getters. Will these provide sufficient guarantees that the object will not be modified ? Are all 3 necessary or 2 of the 3 conditions are more than enough ?
5 Answers
public final class MyClass Has nothing to do with immutability, it only disallows inheritance.
Just marking variable references final is not enough, every object you refer to has to be immutable as well.
final doesn't make an object immutable, it makes the references immutable;
private final List<String> strings = new ArrayList<String>(); strings is still a mutable List, only the reference to the List is immutable.
Be careful with things like:
Collections.unmodifiableList(strings); Collections.unmodifiableList() JavaDoc provides a "unmodifiable view" but it still doesn't guarantee that the underlying list could not be changed by an external reference to the original List that is being wrapped. A deep copy of the list contents would have to be made into a new list and that list wrapped with unmodifiable.
And every instance of every object and all their children and children's children have to be immutable as well.
1 Comment
Marking your fields final is the only option you need out of the mentioned set. You can provide getters, but be careful about mutable sub-objects like collections. Final makes the reference immutable but not the contents. A useful technique with getters is to make a defensive copy if the value is mutable like so:
public class ImmutableExample{ private final int value1; // immutable private final List<Integer> value2; // contents will not be immutable public ImmutableExample(...){...} // be careful here to copy the collection as you want to disalow any outside modification. public int getValue1(){ return value1; } public List<Integer> getValue2(){ return Collections.unmodifiableList(value2); } } Another option is to use Google Guava collections that include Immutable collections as we discussed here: Immutable objects and unmodifiable collections. These result in really easy immutable classes:
public class ImmutableExample{ private final int value1; // immutable private final ImmutableList<Integer> value2; // immutable public ImmutableExample(...){...} public int getValue1(){ return value1; } public List<Integer> getValue2(){ return value2; } } 2 Comments
Marking your class as final you're only telling that no one can subclass it.
That said, marking your variables as final would be enough, as long as your class had only 'primitive' attributes. It's ok (and encouraged) to keep the getters, however, you also may want remove the setters, since while you don't want any external class to mess around with your variables, setters won't be needed.
Comments
It would be more natural to set all variables private and only provide getters. You then need not set the class final which is unnecessary overkill.
Further consideration must be made if the fields are non-final objects themselves such as collections. You should only store copies of these.
Comments
Contrary to some of the comments here, making a class final does have to do with immutability. For a class to be immutable, it should be final, so as to not allow subclasses to override any getters, and thereby not allowing them to "mutate" it by returning a different value.
Recall that to guarantee immutability, a class must not permit itself to be subclassed. - Joshua Bloch, Effective Java also, from the Java tutorials, A Strategy for Defining Immutable Objects:
3.Don't allow subclasses to override methods. The simplest way to do this is to declare the class as final
finalis an option. No need to mark the class asfinalsince this means the class can't be inherited. The only condition your class fields must meet is that they must not change in any way. Note that hacking the state using reflection must not be a factor when considering class immutability.