Skip to main content

Final? Not yet...

Posted by felipeal on February 13, 2007 at 8:11 AM PST

Now that the pandora box is open is open, it is my turn to suggest a (possible worthless, I know) change to the Java language: the semifinal modifier!



I had this idea on my way home last week, after teaching the final keyword for my current Globalcode class. The idea was basically this: final is a well designed modifier, as it has a similar meaning on the many places it can be used (variable, method and class definitions). So wouldn't it be nice if there was a similar modifier for definitions that are not quite final?



Enters the semifinal modifier - too bad I didn't have this idea during the World Cup :-(.



The first and most useful case (if not the *only* useful case at all) is when defining a class: a class (or interface) marked as semifinal can be extended, but its public behavior cannot change. In other words, you can override any method of this class (as long as you do not make then more accessible than the original), but you cannot add any public method (or attribute).



The reasoning? I think such classes could be useful when defining APIs, frameworks, or components, where you do not want to allow extended classes to be more capable than its superclass (a side effect of this new class modifier is that the compiler could generate warnings when a subclass of a semifinal class is used as the type of a reference, parameter or method return). For instance, say you have an interface I that has methods mA() and mB(), and one implementation X that adds a method mC(). Over time, mC() becomes so popular and necessary that the clients of I always cast it to X, in order to call mC(). Well, if this method is so necessary, it should be part of I, and not an implementation detail - declaring I as semifinal would force it (i.e., X could not add a new public method mC()).

Here is an example:


semifinal class SuperClass {
   protected void m1() {
   }
   protected void m2() {
   }
   public void m3() {
   }
}

class SubClass extends SuperClass {

   // declaration below is valid: same access level (protected)
   protected void m1() {
   }

   // declaration below is not valid: more accessible (public)
   public void m2() {
   }

   // declaration below is valid: overriding public method
   public void m3() {
   }

   // declaration below is valid: new method, but private
   private void myMethod() {
   }

   // declaration below is not valid: new public method
   public void myExtendedMethod() {
   }
}

To be honest, I never designed a long-lasting API, so I am not sure it makes sense at all - but if you make an analogy to Java EE specification and vendor-specific features, it might...



Now, if this new modifier is as orthogonal as final, it could be applied to variables and methods as well. In the case of a variable, it means its value must be defined at least once, and at most twice (and not exactly once, as it happens with final). I can see at least two situations where this is helpful:



1.When there is an optional assignment for an nonexistent value . Example:

semifinal MyObject o = getMyObject();
if ( o == null ) {
  o = new MyObject();
}
// from this point on, o is final and cannot be changed

Without semifinal, you have to either create a temporary reference:

final MyObject tmpObject = getMyObject(); // dirty hack
final MyObject o = tmpObject != null ? tmpObject : new MyObject();

Or not use final at all:

MyObject o = getMyObject();
if ( o == null ) {
  o = new MyObject();
}
// o can be changed, as it is not final

2.Before try/catch blocks:
semifinal MyObject o = null;
try {
   o = getMyObject(); // getMyObject() throws NastyException, which is checked
   // from this point on, o is final and cannot be changed
} catch( NastyException e ) {
  throw new NastyRuntimeException( "o is a nasty reference. No dessert for it!", e );
}
// we must use o from this point on, but it should not be changed

Again, without semifinal, we have to use the dirty hack or not use final at all:
MyObject tmpObject = null;
try {
   tmpObject = getMyObject(); // getMyObject() throws NastyException, which is checked
   // from this point on, o is final and cannot be changed
} catch( NastyException e ) {
  throw new NastyRuntimeException( "o is a nasty reference. No dessert for it!", e );
}
final MyObject o = tmpObject;

or

MyObject o = null;
try {
   o = getMyObject(); // getMyObject() throws NastyException, which is checked
   // from this point on, o is final and cannot be changed
} catch( NastyException e ) {
  throw new NastyRuntimeException( "o is a nasty reference. No desert for it!", e );
}
// we must use o from this point on, but it can be changed as it is not final



The final (no pun intended) case is the method definition: a semifinal method means it can be overriden, but at most by the direct subclass (i.e., it cannot be overriden more than once). To be honest, that's the less useful of the three situations (I mean, assuming the other two are any useful at all :-), but it would allow some interesting (not to say weird) possibilities, like this:

protected abstract semifinal void doIt();

Such definition means the method doIt() must be overridden by the subclass (as it is abstract), but is final after that (i.e., it cannot be overridden by a subclass of the subclass). It *might* be useful when implementing the Template Design Pattern, for instance.



Anyway, useful or not, that's just one idea I had in a 10-minutes car ride. I have no plans (neither time :-) to implement it, and it might not be as simple as it is described above - there are probably many corner cases to consider (like inner classes and generics), decisions to be made (for instance, it is clear that a subclass of a semifinal class must be defined as semifinal or final; but what about an overridden semifinal method?) and this is not just compiler fun (it might be in the case case of variables, but for methods and classes, that information must be somehow stored in the byteclass). But if someone has the nerves to give it a try, please let me know...

Related Topics >>