> This applies only to the perfect (final) framework.
> In other words for a framework without bugs and a framework, where all 
> possible usecases are considered by its author.
> 
> I agree that there are dangers, when overwriting methods. But in my 
> experience they rarely matter. Once created methods rarely change in a way 
> that affects subclasses.

Subclassing breaks encapsulation. That's the fundamental reason why you must 
design with care to allow for subclassing, or prohibit it. Making all the 
fields of a class public would give developers increased power -- but of course 
this breaks encapsulation, so we avoid it.

We broke people all the time in Swing. It was very difficult to make even 
modest bug fixes in Swing without breaking somebody. Changing the order of 
calls in a method, broke people. When your framework or API is being used by 
millions of programs and the program authors have no way of knowing which 
version of your framework they might be running on (the curse of a shared 
install of the JRE!), then you find an awful lot of wisdom in making everything 
final you possibly can. It isn't just to protect your own freedom, it actually 
creates a better product for everybody. You think you want to subclass and 
override, but this comes with a significant downside. The framework author 
isn't going to be able to make things better for you in the future.

There's more to it though. When you design an API, you have to think about the 
combinations of all things allowed by a developer. When you allow subclassing, 
you open up a tremendous number of additional possible failure modes, so you 
need to do so with care. Allowing a subclass but limiting what a superclass 
allows for redefinition reduces failure modes. One of my ideals in API design 
is to create an API with as much power as possible while reducing the number of 
failure modes. It is challenging to do so while also providing enough 
flexibility for developers to do what they need to do, and if I have to choose, 
I will always err on the side of giving less API in a release, because you can 
always add more API later, but once you've released an API you're stuck with 
it, or you will break people. And in this case, API doesn't just mean the 
method signature, it means the behavior when certain methods are invoked (as 
Josh points out in Effective Java).

The getter / setter method problem Jonathan described is a perfect example. If 
we make those methods non-final, then indeed it allows a subclass to override 
and log calls. But that's about all it is good for. If the subclass were to 
never call super, then we will be broken (and their app as well!). They think 
they're disallowing a certain input value, but they're not. Or the getter 
returns a value other than what the property object holds. Or listener 
notification doesn't happen right or at the right time. Or the wrong instance 
of the property object is returned.

Two things I really like: final, and immutability. GUI's however tend to favor 
big class hierarchies and mutable state :-). But we use final and immutability 
as much as we can.

Richard

Reply via email to