Reputation: 1174
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
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
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
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