Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why would one declare an immutable class final in Java?

I read that to make a class immutable in Java, we should do the following,

  1. Do not provide any setters
  2. Mark all fields as private
  3. Make the class final

Why is step 3 required? Why should I mark the class final?

like image 625
Anand Avatar asked Sep 06 '12 18:09

Anand


People also ask

Does an immutable class need to be final?

No, it is not mandatory to have all properties final to create an immutable object. In immutable objects you should not allow users to modify the variables of the class. You can do this just by making variables private and not providing setter methods to modify them.

Why are immutable fields final?

An object (class or instance) is immutable, if its internal state cannot be changed (reflection doesn't count). Making a field final guarantees only that the value (if it's a primitive) or reference (for non-primitives) cannot be changed.

Can we create immutable class in Java without final?

The problem with an immutable class not being final is that, subclasses may not be immutable. Here is an example from the Java API, java. lang. String is immutable and final, if a string is passed to one of your methods you can be sure that it will remain in a consistent state.

Why do we need immutable classes?

Immutable classes make sure that values are not changed in the middle of an operation without using synchronized blocks. By avoiding synchronization blocks, you avoid deadlocks. And since you are always working with an unchangeable consistent state, you avoid race conditions.


2 Answers

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!

like image 58
templatetypedef Avatar answered Sep 30 '22 00:09

templatetypedef


Contrary to what many people believe, making an immutable class final is not required.

The standard argument for making immutable classes final is that if you don't do this, then subclasses can add mutability, thereby violating the contract of the superclass. Clients of the class will assume immutability, but will be surprised when something mutates out from under them.

If you take this argument to its logical extreme, then all methods should be made final, as otherwise a subclass could override a method in a way that doesn't conform to the contract of its superclass. It's interesting that most Java programmers see this as ridiculous, but are somehow okay with the idea that immutable classes should be final. I suspect that it has something to do with Java programmers in general not being entirely comfortable with the notion of immutability, and perhaps some sort of fuzzy thinking relating to the multiple meanings of the final keyword in Java.

Conforming to the contract of your superclass is not something that can or should always be enforced by the compiler. The compiler can enforce certain aspects of your contract (eg: a minimum set of methods and their type signatures) but there are many parts of typical contracts that cannot be enforced by the compiler.

Immutability is part of the contract of a class. It's a bit different from some of the things people are more used to, because it says what the class (and all subclasses) can't do, while I think most Java (and generally OOP) programmers tend to think about contracts as relating to what a class can do, not what it can't do.

Immutability also affects more than just a single method — it affects the entire instance — but this isn't really much different than the way equals and hashCode in Java work. Those two methods have a specific contract laid out in Object. This contract very carefully lays out things that these methods cannot do. This contract is made more specific in subclasses. It is very easy to override equals or hashCode in a way that violates the contract. In fact, if you override only one of these two methods without the other, chances are that you're violating the contract. So should equals and hashCode have been declared final in Object to avoid this? I think most would argue that they should not. Likewise, it is not necessary to make immutable classes final.

That said, most of your classes, immutable or not, probably should be final. See Effective Java Second Edition Item 17: "Design and document for inheritance or else prohibit it".

So a correct version of your step 3 would be: "Make the class final or, when designing for subclassing, clearly document that all subclasses must continue to be immutable."

like image 41
Laurence Gonsalves Avatar answered Sep 29 '22 23:09

Laurence Gonsalves