Ready, Aim, Final
There're
some interesting rants going on about Java's
final keyword. And Mike Bowler does a decent job of
explaining what final does. As Mike points out, all of the ranting is about whether you should declare classes and methods final.
The arguments against declaring classes and methods final are persuasive. It makes it really hard to unit test anything. You are putting future programmers in a straightjacket (that's actually part of the spirit of bondage and discipline languages like Java, but that doesn't mean it's a good idea). And some refactoring becomes impossible.
When I look at these arguments, I see evidence of a deeper malaise. The problem isn't the final keyword. The problem is
the lack of separation between interfaces and implementations.
Take
java.lang.String. This class is final, and that is a royal PITA. If you want to extend String to do neat things like keep track of SQL injection safety you are SOL. Now if there was a String
interface, and everyone declared their instance variables with the interface instead of the implementation, there would be no problem with a final String class.
Do you need a flag for whether the String is SQL-safe? Use delegation instead of inheritence. No problem. Do you need pluggable storage behaviours for handling special character sets? Use aggregation and a Strategy pattern.
Separating the String interface from the String implementation solves our problems nicely.
I'm 100% ok with the final keyword in implementations. I think it's ok to make a class and say, this is the FizbangImplementationClass. For example, if you were making a sort class for some reason, it would be a little weird to override the actual sort algorithm. If you do, maybe it isn't really a-kind-of FizbangImplementationClass, so it shouldn't extend that base class. It ought to
conform to the same interface but be implemented as something else.
At no time in the near future will I be proposing JSR666, "How to fix the Java language so that you can write elegant, object-oriented code." But if I do, my suggestion will be to
make every class an interface automatically. (This is not an original idea.)
Here's how it would work. We keep all of the existing syntax exactly as we have it. But the first addition is that you can have class A implement class B. Note that class B is a class, not an interface. This means class A acquires the public members of class B but not their implementations. Now you can 'override' to your heart's content. The final keywords do not affect this, because you are actually implementing an interface.
Is this bad? I don't think so. Framework authors that declare methods final are usually trying to ensure some consistency within a class. If you are implementing the class instead of extending it, you can build it from the ground up. If you need some of the class's behaviour you can use delegation and the orginal class will retain its original behaviour.
The second addition is that when you declare a variable to be of a certain class, you are naturally declaring it to be the interface of the class. So anything declared as a String is actually declared as the String interface, not the String class.
(There are other, subtle changes that are needed. Should String.class refer to the class and String.interface refer to the interface separately? Order plenty of espresso and let the committee thrash this kind of question out).
This could be all accomplished within the current language if everyone was disciplined about declaring interfaces. However, Gosling and Steele set the tone for the Java language when the original libraries contained precious few interfaces and tons of classes. I think it's too late to get everyone to change. So some language change is needed to handle the zillions of lines of existing code.
Or, you could take another approach and eschew languages that deliberately restrict your expressiveness. It's a big world out here, and it would be a shame if you don't get out from behind your desk and travel once in a while :-)
Updates: Elliote Rusty Harold triple-underscored his support for the final keyword, while suggesting that interfaces are mostly bad, and I responded with respect for our differences.
Then Rusty provided some excellent reasons for locking interfaces down in his designs, and I explained why I believe there're legitimate reasons to choose Rusty's approach for some programmers and more flexibility for others.
Labels: java, popular