Final methods
There are two reasons for final methods. The first is to put a lock on the method to prevent any inheriting class from changing its meaning. This is done for design reasons when you want to make sure that a methods behavior is retained during inheritance and cannot be overridden.
The second reason for final methods is efficiency. If you make a method final, you are allowing the compiler to turn any calls to that method into inline calls. When the compiler sees a final method call, it can (at its discretion) skip the normal approach of inserting code to perform the method call mechanism (push arguments on the stack, hop over to the method code and execute it, hop back and clean off the stack arguments, and deal with the return value) and instead replace the method call with a copy of the actual code in the method body. This eliminates the overhead of the method call. Of course, if a method is big, then your code begins to bloat, and you probably wont see any performance gains from inlining, since any improvements will be dwarfed by the amount of time spent inside the method. It is implied that the Java compiler is able to detect these situations and choose wisely whether to inline a final method. However, its best to let the compiler and JVM handle efficiency issues and make a method final only if you want to explicitly prevent overriding.[31]
final and private
Any private methods in a class are implicitly final. Because you cant access a private method, you cant override it. You can add the final specifier to a private method, but it doesnt give that method any extra meaning.
This issue can cause confusion, because if you try to override a private method (which is implicitly final), it seems to work, and the compiler doesnt give an error message:
//: c06:FinalOverridingIllusion.java
// It only looks like you can override
// a private or private final method.
import com.bruceeckel.simpletest.*;
class WithFinals {
// Identical to "private" alone:
private final void f() {
System.out.println("WithFinals.f()");
}
// Also automatically "final":
private void g() {
System.out.println("WithFinals.g()");
}
}
class OverridingPrivate extends WithFinals {
private final void f() {
System.out.println("OverridingPrivate.f()");
}
private void g() {
System.out.println("OverridingPrivate.g()");
}
}
class OverridingPrivate2 extends OverridingPrivate {
public final void f() {
System.out.println("OverridingPrivate2.f()");
}
public void g() {
System.out.println("OverridingPrivate2.g()");
}
}
public class FinalOverridingIllusion {
private static Test monitor = new Test();
public static void main(String[] args) {
OverridingPrivate2 op2 = new OverridingPrivate2();
op2.f();
op2.g();
// You can upcast:
OverridingPrivate op = op2;
// But you can't call the methods:
//! op.f();
//! op.g();
// Same here:
WithFinals wf = op2;
//! wf.f();
//! wf.g();
monitor.expect(new String[] {
"OverridingPrivate2.f()",
"OverridingPrivate2.g()"
});
}
} ///:~
Overriding can only occur if something is part of the base-class interface. That is, you must be able to upcast an object to its base type and call the same method (the point of this will become clear in the next chapter). If a method is private, it isnt part of the base-class interface. It is just some code thats hidden away inside the class, and it just happens to have that name, but if you create a public, protected, or package-access method with the same name in the derived class, theres no connection to the method that might happen to have that name in the base class. You havent overridden the method; youve just created a new method. Since a private method is unreachable and effectively invisible, it doesnt factor into anything except for the code organization of the class for which it was defined.