The output shown above demonstrates how a subclass can misrepresent its parent's advertised behavior. This can happen anytime implementation inheritance is involved, but it is more insidious when we have reason to believe the class involved is immutable. We could mark the methods as final
to reduce much of this, but the constructor still has issues. Furthermore, marking all the parent class's methods as final
rather than simply marking the entire class as final
has some drawbacks. These include the potential conundrum about how to handle methods (such as toString()
) that really should not be final
and the risk of new methods being added to the parent class without the developer remembering or knowing to mark them as final
.
In the DemonstrationMain
class, I also made use of a custom class called RenegadeBigDecimal
. The purpose of this class is to demonstrate the abuse of a subclass of BigDecimal. The code listing for RenegadeBigDecimal
is shown next.
RenegadeBigDecimal.java
package dustin.examples;
import java.math.BigDecimal;
/**
* Demonstrating the troubles with non-final 'immutable' classes.
*/
public class RenegadeBigDecimal extends BigDecimal
{
/**
* Parameterized constructor accepting Long for instantiation of BigDecimal.
*
* @param val Long parameter intended, but not actually used, for
* instantiation of BigDecimal.
*/
public RenegadeBigDecimal(final Long val)
{
super(-1);
}
/**
* Example of intentionally abusing behavior likely expected for an
* immutable class by the subclass.
*
* @param divisor Divisor in division process.
*/
public BigDecimal divide(final BigDecimal divisor)
{
return this;
}
}
This last example shows how easy it is to abuse BigDecimal
. Because the divide method was overridden to return itself, it is obvious that neither the BigDecimal
class nor its method divide
were designated as final
. Even if the divide
method would have been made final, the constructor could still have implemented bad intent and any client code thinking it was a BigDecimal
would pay the consequences.
Trade-offs and Drawbacks of final Immutable Classes
There may be times when the ability to override a "largely immutable" or "mostly immutable" class is more important than having a "completely immutable" or "strongly immutable" class. Software design and development is full of trade-offs and this is just another one of those. The benefits of different degrees of mutability can be weighed against the costs. I believe it is no coincidence that Joshua Bloch changed the title of his Effective Java item on immutable objects from "Favor Immutability" in Edition 1 to "Minimize Mutability" in Edition 2. I think this is most readily done by starting with a class that is completely or strongly immutable and only relaxing its mutability in different ways when justified by the benefits of doing so.
Again, at least in my mind, it is no coincidence that the creators of the recently announced Noop project have stated that they wish to encourage immutability and that they wish to discourage implementation inheritance. I think in many of the cases in which implementation inheritance might be desired, it might be better to simply use composition to share common, immutable data and behaviors between two highly related, possibly immutable, classes. That advice doesn't sound particularly different than what many others have suggested before in terms of all classes, including mutable classes.
Conclusion
The safest way to make a class "strongly immutable" is to designate the entire class as final
. Doing so ensures that no mistakes will be made in creating new methods in the future in the immutable class that are not marked final
and can be carelessly or maliciously overridden. The final
keyword applied at class level also prevents subclasses from "masquerading" as their immutable parent with very different behavior than that expected for the "immutable" parent class.
Additional References
⇒ Mutable and Immutable Objects
⇒ StackOverflow: immutable class should be final?
⇒ OBJ38-J: Immutable Classes Must Prohibit Extension
⇒ Bug 4617197 - RFE: Add Immutable types to Java
⇒ Some Concurrency Tips (talks about benefits of immutable objects)
⇒ The Final Word on the final Keyword
This story, "Is a Java Immutable Class Always final?" was originally published by JavaWorld.