Why Would One Declare an Immutable Class Final in Java

Why would one declare an immutable class final in Java?

If you don't mark the class final, it might be possible for me to suddenly make your seemingly immutable class actually mutable. For example, consider this code:

public class Immutable {
private final int value;

public Immutable(int value) {
this.value = value;
}

public int getValue() {
return value;
}
}

Now, suppose I do the following:

public class Mutable extends Immutable {
private int realValue;

public Mutable(int value) {
super(value);

realValue = value;
}

public int getValue() {
return realValue;
}
public void setValue(int newValue) {
realValue = newValue;
}

public static void main(String[] arg){
Mutable obj = new Mutable(4);
Immutable immObj = (Immutable)obj;
System.out.println(immObj.getValue());
obj.setValue(8);
System.out.println(immObj.getValue());
}
}

Notice that in my Mutable subclass, I've overridden the behavior of getValue to read a new, mutable field declared in my subclass. As a result, your class, which initially looks immutable, really isn't immutable. I can pass this Mutable object wherever an Immutable object is expected, which could do Very Bad Things to code assuming the object is truly immutable. Marking the base class final prevents this from happening.

Hope this helps!

Why final keyword is necessary for immutable class?

As stacker says, final makes sure the class isn't subclassed. That's important so that any code which is relying on its immutability can do so safely.

For example, immutable types (where each field is also of an immutable type) can be freely used between threads without worrying about data races etc. Now consider:

public class Person {
private final String name;

public Person(String name) {
this.name = name;
}

public String getName() {
return name;
}
}

That looks like you can share Person instances freely across threads with no problem. But what about when the object you're sharing is actually a mutable subclass:

public class Employee extends Person {
private String company;

public Employee(String name, String company) {
super(name);
this.company = company;
}

public void setCompany(String company) {
this.company = company;
}

public String getCompany() {
return company;
}
}

Now instances of Employee aren't safe to share between threads, because they're not immutable. But the code doing the sharing may only know about them as instances of Person... leading them into a false sense of security.

The same goes for caching - it should be safe to cache and reuse immutable types, right? Well, it is safe to cache instances which are genuinely of an immutable type - but if you're dealing with a type which itself doesn't allow mutation, but does allow subclasses, it's suddenly not safe any more.

Think about java.lang.Object. It doesn't have any mutable fields, but it's clearly a bad idea to treat every Object reference as if it's a reference to an immutable type. Basically it depends on whether you think about immutability as a property of the type or of objects. A truly immutable type declares "any time you see a reference of this type, you can treat it as immutable" - whereas a type which allows arbitrary subclassing can't make that claim.

As an aside, there's a half-way house: if you can limit the subclassing to only "trusted" places, you can ensure that everything's immutable, but still allow that subclassing. The access in Java makes that tricky, but in C# for example you could have a public class which only allowed subclassing within the same assembly - giving a public API which is nice and strong in terms of immutability, while still allowing for the benefits of polymorphism.

why fields should be final in immutable class?

If you read

private final String name;

you know the field is immutable.

If you read

private String name;

you have to read the entire class to check it is not changed anywhere. This is means much more work for you.

You may remember now, having just written the class that you didn't add a setter, but after writing many more classes you read your own class six month later, you won't remember reliably.

Even if it is not changed now, someone (possibly yourself) could change it later by adding code. However, you might have made the assumption the value won't change.

In short, only make it non-final when you mean the value to change, and make it final when you didn't expect it to change. Don't leave it as a may be/may be not.


Now imagine you are used to being clear about which fields can be changed and which cannot. This saves you a lot of work when reading some else's code. But you discover that you are reading code which is not clear and non-final doesn't mean it was changed, it now means you have to check things, you wouldn't normally have to check which is one more headache in trying to understand some code you really don't need.


A simple example of how much harder it is to read code to determine if a field is effectively final.

public class A {
static class B {
private int x;
}

// some code

This all looks fine up to this point, no setters or even methods in B. So B.x is immutable right?

    static class C {
public void update(B b, int x) {
b.x = x; // this really compiles
}
}
}

Oops no, you have to read the whole class file.

It is far better for you to make every field you can final (which should have been the default IMHO) when you write the code, rather than leaving it for someone to figure out later.

Immutable Java class with non-final member

Is Person immutable?

No it isn't.

An immutable class is final and only has final members.

In your case, what you want to use is a builder class:

final Person person = new PersonBuilder().withFather(xx).withMother(xx).build();

This way you can make all members of Person final, and since Person is itself final, you get a real immutable class.

non final - immutable classes

If you can extend an immutable class (which means it's not final), you can add mutable properties to the sub-class, which would make your sub-class mutable, and therefore the base class would also be mutable, since it can have mutable sub-classes.

Immutable class?

What is an immutable object?

An immutable object is one that will not change state after it is instantiated.

How to make an object immutable?

In general, an immutable object can be made by defining a class which does not have any of its members exposed, and does not have any setters.

The following class will create an immutable object:

class ImmutableInt {
private final int value;

public ImmutableInt(int i) {
value = i;
}

public int getValue() {
return value;
}
}

As can be seen in the above example, the value of the ImmutableInt can only be set when the object is instantiated, and by having only a getter (getValue) the object's state cannot be changed after instantiation.

However, there must be care taken that all objects that are referenced by the object must be immutable as well, or it could be possible to change the state of the object.

For example, allowing an reference to an array or ArrayList to be obtained through an getter will allow the internal state to change by changing the array or collection:

class NotQuiteImmutableList<T> {
private final List<T> list;

public NotQuiteImmutableList(List<T> list) {
// creates a new ArrayList and keeps a reference to it.
this.list = new ArrayList(list);
}

public List<T> getList() {
return list;
}
}

The problem with the above code is, that the ArrayList can be obtained through getList and be manipulated, leading to the state of the object itself to be altered, therefore, not immutable.

// notQuiteImmutableList contains "a", "b", "c"
List<String> notQuiteImmutableList= new NotQuiteImmutableList(Arrays.asList("a", "b", "c"));

// now the list contains "a", "b", "c", "d" -- this list is mutable.
notQuiteImmutableList.getList().add("d");

One way to get around this problem is to return a copy of an array or collection when called from a getter:

public List<T> getList() {
// return a copy of the list so the internal state cannot be altered
return new ArrayList(list);
}

What is the advantage of immutability?

The advantage of immutability comes with concurrency. It is difficult to maintain correctness in mutable objects, as multiple threads could be trying to change the state of the same object, leading to some threads seeing a different state of the same object, depending on the timing of the reads and writes to the said object.

By having an immutable object, one can ensure that all threads that are looking at the object will be seeing the same state, as the state of an immutable object will not change.

What is the difference between immutable and final in java?

final means that you can't change the object's reference to point to another reference or another object, but you can still mutate its state (using setter methods e.g). Where immutable means that the object's actual value can't be changed, but you can change its reference to another one.

Concerning the second part of your question (immutability part), the compiler creates a new String object with the value of "Sam", and points the name reference to it.

Immutable Classes and Subclasses

If you want to enforce immutability, you cannot have subclasses.

This is almost true, but not entirely. To restate it:

If you want to enforce immutability, you must ensure that all sub-classes are immutable.

The problem with allowing subclassing is that normally anyone who can author a class can subclass any public non-final class.

But all subclasses must invoke one of their super-class's constructors. Package-private constructors can only be invoked by subclasses in the same package.

If you seal packages so that you control which classes are in your package, you can constrain subclassing. First define a class you want to subclass:

public abstract class ImmutableBaseClass {
ImmutableBaseClass(...) {
...
}
}

Since all sub-classes have to have access to the super-constructor, you can ensure all the sub-classes in the package you define follow immutable discipline.

public final class ImmutableConcreteClass extends ImmutableBaseClass {
public ImmutableConcreteClass(...) {
super(...);
}
}

To apply this to your example,

public abstract class Employee {
private final Id id;
private final Name name;

// Package private constructor in sub-classable class.
Employee(Id id, Name name, ...) {
// Defensively copy as necessary.
}
}

public final class Accountant extends Employee {
// Public constructos allowed in final sub-classes.
public Accountant(Id id, Name name, ...) {
super(id, name, ...); // Call to super works from same package.
}
}

public final class ITWorker extends Employee {
// Ditto.
public ITWorker(Id id, Name name, ...) {
super(id, name, ...);
}
}


Related Topics



Leave a reply



Submit