Why is almost everything in the API final

Richard Bair richard.bair at oracle.com
Mon Sep 2 12:01:52 PDT 2013


> 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


More information about the openjfx-dev mailing list