iAmLearning
iAmLearning

Reputation: 1174

Immutable classes in java

Is it required to make the fields of an Immutable class private as if they are marked as final , can not be changed ?
I mean isn't it enough to just mark the fields as final ?

(I know that it's not necessary for an immutable class to have final fields but it's advisable to have it for compile time check for the class internally.)

Upvotes: 1

Views: 331

Answers (3)

Boris the Spider
Boris the Spider

Reputation: 61148

No this is not enough.

Consider this example:

final class ImmutableClass {
    private final List<String> data;

    public ImmutableClass(final List<String> data) {
        this.data = data;
    }

    public List<String> getData() {
        return data;
    }
}

This class is final so cannot be extended. It's data field is final so cannot be changed after it is assigned.

But:

final ImmutableClass immutableClass = new ImmutableClass(data);
immutableClass.getData().add("Some other value");

Oops.

So to make a class truly immutable all fields should also be immutable classes, or have defensive copies.

For example, to correct the issues about you would need to do this:

final class ImmutableClass {
    private final List<String> data;

    public ImmutableClass(final List<String> data) {
        this.data = new ArrayList<>(data);
    }

    public List<String> getData() {
        return Collections.unmodifiableList(data);
    }
}

And this only works because String is immutable. If you had, for instance, a List<Date> you would also need to copy the individual Date objects.

Upvotes: 7

Philipp
Philipp

Reputation: 69663

Having private variables accessed through getters gives you more freedom with the implementation of the class. You can, for example, replace a trivial getter with an implementation which calculates or retrieves the value on access or caches the result.

Another reason to keep fields as private even when they are final is when the field is a mutable object. In that case the object can be changed even though it is final which is usually not what you want. Here is an example of a presumably immutable class with a public final field of the mutable Date class.

 class DateHolder {
      public final Date date;
      DateHolder(Date date) {
           this.date = date:
      }
 }

 // ...

 DateHolder holder = new DateHolder(Date.now());

 // this doesn't work because date is final:
 //holder.date = new Date(2013, 11, 23);

 // but this works even though date is final:
 holder.date.setYear(2013);

Upvotes: 1

user1804599
user1804599

Reputation:

final fields can only be assigned to from within the constructor. Therefore, making fields final is enough to make them immutable after construction. Assigning pointers to mutable objects to them is a different story.

Upvotes: 1

Related Questions