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

Reply via email to