Sep 22 2005
Why is finalize method protected?
Rakesh sent me a mail the other day asking why the finalize
method is protected
. Well, here’s what I think:
The finalize
method is invoked by the JVM/GarbageCollector on Objects which are no longer referenced. So, if I were the guy who designed the finalize
method, ideally I would want it to be private
. Just like why the writeObject method of the Serializable interface is private. These methods are not meant to be called by other user objects, these methods are invoked only by the JVM runtime classes only. So it would make sense to make them private, and have special handling in the VM to invoke these methods. This is ok in case of the writeObject method, however, making finalize
method private
would have other implications.
Assume we had decided to go ahead and allow the finalize
method to be private. Consider the following classes
public class SuperClass { private HeavyResource resource = new HeavyResource(); private void finalize() throws Throwable { resource.shutdown(); } } public class SubClass extends SuperClass { private AnotherResource another = new AnotherResource(); public SubClass(){ super(); //Added for clarity } private void finalize() throws Throwable { another.shutdown(); } }
Since SubClass extends the SuperClass, when we create an instance of SubClass, we would also have created an instance of HeavyResource and an instance of AnotherResource. However, when this instance of SubClass is being finalized, we shutdown only the instance of AnotherResource. The shutdown method of HeavyResource would not be called.
The recommended practice for finalize
methods is that in the finally
block of the finalize
method one should call the finalize
method of the super class. (That’s right, in the constructor the first statement has to be the call to super so for destructor/finalize the sequence should be reversed and super should be called last). So our SubClass would look something like:
public class SubClass extends SuperClass { private AnotherResource another = new AnotherResource(); public SubClass(){ super(); //Added for clarity } private void finalize() throws Throwable { try{ another.shutdown(); }finally{ super.finalize();//Compilation error here } } }
This would have worked, but unfortunately this won’t even compile. You can not call the private method of the super class. There are couple of ways I can think of to solve this problem.
One solution of-course would be to relax our requirements and make the access modifier of the finalize
method protected and hope people are sensible enough not to call it!
Another solution could be to allow calls to super.finalize()
, even if the finalize
method of the super class has private access modifier, in the Java Language Specification (JLS).
My preferred solution would be to have compilers automatically add a try finally block and insert a call to super.finalize()
method in the finally block. (Modifying byte code to add a try finally block is a nightmare (as compared to adding a call to super()
), but that’s a separate discussion!) This would be similar and consistent with the way compilers add the call to super()
as the first statement of a constructor if it does not already exist. (You can use javap -c ClassName
to look at the byte code generated by the compiler, but I prefer to use JClasslib.)
The Java language specification, (3rd ed, Section 12.6) does mention that the call to super.finalize
is not injected automatically and provides a hint as to why:
The fact that class
Object
declares afinalize
method means that thefinalize
method for any class can always invoke thefinalize
method for its superclass. This should always be done, unless it is the programmer’s intent to nullify the actions of the finalizer in the superclass. (Unlike constructors, finalizers do not automatically invoke the finalizer for the superclass; such an invocation must be coded explicitly.)
It appears this was done “So that the programmer can nullify the actions of the finalizer in the superclass.” But thanks to this choice, developers today use tools like PMD which warn them about empty finalize
methods and when the finalize
does not call the same method of the super class.
Update (16 Jan 2007): It appears that the designers of C# learnt from Java’s mistakes and decided to make constructor and destructor symmetric. In C#, the destructor of the super-class is called whether the destructor of the sub-class was successfuly completed or not. From “Section 16.3: How exceptions are handled” of the C# language specification 1.2:
Exceptions that occur during destructor execution are worth special mention. If an exception occurs during destructor execution, and that exception is not caught, then the execution of that destructor is terminated and the destructor of the base class (if any) is called. If there is no base class (as in the case of the object type) or if there is no base class destructor, then the exception is discarded.
September 22nd, 2005
5:20 pm GMT-0700
Comment #1