Death to Final
From crispyneurons
9 April 2003
Jason Wells
[edit] Abstract
The Java programming language contains the keyword final. In this paper, I argue that this keyword is worse than useless, and actually causes programming problems rather than preventing them. A technique for bypassing the restrictions of this keyword is also provided, and its consequences are explored.
[edit] What is 'final'?
Java provides the keyword 'final' to prevent programmers from extending or subclassing a particular class, or from overriding a particular method. For example, in Example 1 below, the class SupposedlyFinalClass is declared to be final:
public final class SupposedlyFinalClass {
Example 1. Class definition for SupposedlyFinalClass.
public void doSomething() {
System.out.println("doing something.");
}
}
It is not possible for a programmer to extend SupposedlyFinalClass without removing that keyword. This is often impractical or even impossible, especially if a programmer is using a binary third-party library, or libraries for which they have no source code access. For examples of this, refer to the javadocs for the most central java API there is: java.lang. In this package, such critically important classes as String and StringBuffer are declared final. This seriously restricts the programmer's options. For example, if I want to create a LocalizedString class that extends String, too bad. No formal specialization is permitted.
[edit] Definalizing 'final'
This would be a serious problem for Java if that were the last word on the subject. If it were truly impossible to specialize final classes, Java would be a sorry language indeed. Fortunately there is a way around this problem, to effectively 'definalize' final. The trick is to employ composition and the Adapter design pattern.
Composition simply refers to containing one object within another. While it is impossible to formally extend SupposedlyFinalClass, nothing prevents composing it within another class. In Example 2, we compose it in a class called ExtendedFinalClass:
public class ExtendedFinalClass {
Example 2. Class definition for ExtendedFinalClass.
SupposedlyFinalClass sfc;
public void doSomething() {
sfc.doSomething();
System.out.println("also doing something more.");
}
public void doSomethingElse() {
System.out.println("doing something else.");
}
public ExtendedFinalClass() {
sfc = new SupposedlyFinalClass();
}
}
Composition opens the door, but the Adapter design pattern lets us walk through it. While it is usually used to convert the interface of one class to another interface, in this case we're using it differently. We want to make the Adapter class have precisely the same interface as the original class, as well as adding needed specializations. We see that ExtendedFinalClass does exactly that.
While this solution can get the job done, we pay many penalties for having to use this workaround instead of doing it the right way. The class definition is much more complex than it would have to be if we could use traditional object inheritance. Also, since ExtendedFinalClass doesn't actually extend SupposedlyFinalClass, we can't up-cast ExtendedFinalClass to SupposedlyFinalClass. Which is lame; if we ever encounter a method that takes SupposedlyFinalClass as an argument, we can't ever use ExtendedFinalClass instead.
[edit] Conclusion
To declare a class as final is an extremely arrogant thing to do as a programmer. It implies that the author of the final class has total awareness of every conceivable use of the class, and has decided in no circumstance should anyone be able to specialize or augment the class.
As design-time arrogance is the main reason a programmer would consider using the final keyword, and since it is so trivially (if inelegantly) bypassed, elimination of the final keyword from the language becomes the most desirable solution.
Jason Wells is a philosophy student at UCSD and the VP of Natural Language Products at Semantic Research, Inc.
