In the early days when people were thinking about traditional programming, trying to control overrides with final seemed to make as much sense as controlling access with private or protected. Then the environment evolved, and containers like Spring and J2EE began to add AOP features like proxy generation, automatic transaction management, and so on. The easy way to add aspects is to automatically generate a class that extends the target class, with methods that override, intercept calls, provide additional functions, and then call the overridden method in the super class. Declaring a class or method final interferes with the automatic function of such containers.
Now strictly speaking, you can achieve some of the same results by building a wrapper class that hides an instance of the target class and methods with the same names and arguments that perform extra processing and then delegate the call to the corresponding methods in the target class. However, since this generated class is not an extension of the target, this only works if the programmer created an interface that the generated class can also implement (or an abstract ancestor that it can extend), and if all dependency references are declared to be of the interface type and not the implementation class so the wrapper class can substitute for the original. Although I have been complaining about final for as long as CAS 3 has existed, let me switch sides for a moment (since you don't really understand unless you can see both sides of an argument). It is an entirely reasonable software engineering objective to block access to internal behavior that would be exposed by an override. This is simply a special case of "information hiding". However, in any class that exposes a "public" behavior (where the class is used by another package, is an injected dependency, or performs any plausibly modularized function) then there must be an interface (or abstract superclass), and all references to the class must be declared to be of the interface type, not the implementation class type. Then the programmer who writes the implementation can be guaranteed that nobody can promiscuously extend his methods, but anyone who wants to do so can accomplish the same thing by a wrapper/delegation strategy which intercepts calls to the public behavior without obtaining access to the protected (by final) symbols of the original class. This is a bit more work than extension, but you get better information hiding in exchange for the work. If the original class is a Spring bean that gets dependency injection, there are no further issues. If not (unlikely in the CAS code, but possible in other styles of programming) then programmers should get used to creating Factory classes instead of using "new" directly and obtaining objects from the factory by passing the interface/abstract type instead of direct reference to the implementation class name (or else obtain a name from the environment and do Class.forName, the point being that being able to override or wrap a class is no good unless you can insert your new class in place of the old one without changing the base code). Now we get to the "put your money where your mouth is" moment. Some classes are just internal to a component and do simple things that you may never imagine anyone wanting to change. This is the "private" internal behavior of a component (or package if you prefer) and since it is not considered "public" behavior it would be stupendously complicating if every such internal class had to have an interface and a factory. Such classes do not require an interface (because they are internal and private) but then they should not be final (because if they are so uncontroversial and obvious, nobody in their right mind would ever want to extend them). That then becomes the social contract. If a class is public enough that you want to prohibit extension with final, then you have to provide an interface. If it is obscure and internal so you don't need an interface, then it doesn't need an interface but should not be protected by final. Declaring something final but not using an interface is anti-social. -----Original Message----- I agree with you and your quote from the PDF. The onus is on the overriding code to work properly, not on the overridden code to be unbreakable. It's good to minimize scope and state, using private and even static methods where possible. But, "final" isn't as useful in open source, despite Bloch's Effective Java item 15. -- You are currently subscribed to [email protected] as: [email protected] To unsubscribe, change settings or access archives, see http://www.ja-sig.org/wiki/display/JSG/cas-dev
