(This is a snapshot of my old weblog. New posts and selected republished essays can be found at raganwald.com.)

Thursday, May 25, 2006
  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: ,


Comments on “Ready, Aim, Final:
The original motivation, if I understand it correctly, was security. Remember Java Applets? You don't want one piece of code passing a core object to another without understanding all the side-effects of the API. You finalize classes to close them down and ensure the API always does what it says it would.

A language that doesn't let you extend String, is a language that's asking for complexity. It forces you to work harder because of two assumptions it makes: a) you can't be trusted, b) the language authors know best. So it's asking you to prove your self at every single statement you make, it's intentionally making you jump through hoops.

Those two assumptions were actually a Good Thing(tm) when Java came on the scene to solve the RIA problem. The use of finals was wisely selected in the core libraries. But Applets never quite took over the world. The restricted model remains to this day because old believes die hard.
It should be noted also that in some cases there are some optimizer benfits to using the final keyword. The JIT and JVM can do some nifty rewrites when it knows that a particular item is assured not to change for its life time duration. This doesn't speak directly to finalizing a class such that it can not longer be extended - but rather using the final keyword in practice when doing code in the normal course of a day. Just an extra $0.02 (US).
With respect to optimization:

Compiler hints are generally a bad idea. They encourage premature optimization. Our experience with memory management is now that the VM can do a better job of managing memory dynamically than we can writing explicit malloc and free calls.

The final keyword does help the compiler generate direct method calls and bypass the lookup table in some cases, true.

I suggest that this kind of optimization belongs in the JIT, not the bytecode compiler.

Nevertheless, my so-called proposal does allow the programmer to retain the final keyword for implementation inheritence. I think that even in the presence of interfaces the JIT can use it as an optimization hint if it cannot optimize lookups away.
"If you want to extend String to do neat things like keep track of SQL injection safety you are SOL."

LOL. Already taken care of. See PreparedStatement.

Also, see Java 1.5 CharSequence over a interface for Strings.
I know what a PreparedStatement is, however take a close look: its security rests on the assumption that each query has both a trusted part, the query's pattern, and untrusted parts, the query's parameters.

If you want to go beyond this model, you must be prepared to roll you own.

The same thing applies to XML and HTML serialization and a host of other problems. You can always always use an existing library routine that handles a particular case in a particular way. That's the Java culture.

But it is often difficult to roll your own style. That's also the java culture.

<< Home
Reg Braithwaite

Recent Writing
Homoiconic Technical Writing / raganwald.posterous.com

What I‘ve Learned From Failure / Kestrels, Quirky Birds, and Hopeless Egocentricity

rewrite_rails / andand / unfold.rb / string_to_proc.rb / dsl_and_let.rb / comprehension.rb / lazy_lists.rb

IS-STRICTLY-EQUIVALENT-TO-A / Spaghetti-Western Coding / Golf is a good program spoiled / Programming conventions as signals / Not all functions should be object methods

The Not So Big Software Design / Writing programs for people to read / Why Why Functional Programming Matters Matters / But Y would I want to do a thing like this?

The single most important thing you must do to improve your programming career / The Naïve Approach to Hiring People / No Disrespect / Take control of your interview / Three tips for getting a job through a recruiter / My favourite interview question

Exception Handling in Software Development / What if powerful languages and idioms only work for small teams? / Bricks / Which theory fits the evidence? / Still failing, still learning / What I’ve learned from failure

The unary ampersand in Ruby / (1..100).inject(&:+) / The challenge of teaching yourself a programming language / The significance of the meta-circular interpreter / Block-Structured Javascript / Haskell, Ruby and Infinity / Closures and Higher-Order Functions

Why Apple is more expensive than Amazon / Why we are the biggest obstacles to our own growth / Is software the documentation of business process mistakes? / We have lost control of the apparatus / What I’ve Learned From Sales I, II, III

The Narcissism of Small Code Differences / Billy Martin’s Technique for Managing his Manager / Three stories about The Tao / Programming Language Stories / Why You Need a Degree to Work For BigCo

06/04 / 07/04 / 08/04 / 09/04 / 10/04 / 11/04 / 12/04 / 01/05 / 02/05 / 03/05 / 04/05 / 06/05 / 07/05 / 08/05 / 09/05 / 10/05 / 11/05 / 01/06 / 02/06 / 03/06 / 04/06 / 05/06 / 06/06 / 07/06 / 08/06 / 09/06 / 10/06 / 11/06 / 12/06 / 01/07 / 02/07 / 03/07 / 04/07 / 05/07 / 06/07 / 07/07 / 08/07 / 09/07 / 10/07 / 11/07 / 12/07 / 01/08 / 02/08 / 03/08 / 04/08 / 05/08 / 06/08 / 07/08 /