Previous | Table of Contents | Next |
14.3.7.1. Overriding Is Not Shadowing
Although Java treats the variables and methods of a class analogously in many ways, method overriding is not like variable shadowing at all: You can refer to shadowed variables simply by casting an object to the appropriate type. You cannot invoke overridden methods with this technique, however. Example 14.18 illustrates this crucial difference.
EXAMPLE 14.18. Method overriding versus variable shadowing.
class A { int i = 1; int f() { return i; } } class B extends A { int i = 2; // Shadows variable i in class A. int f() { return -i; } // Overrides method f in class A. } public class override_test { public static void main(String args[]) { B b = new B(); System.out.println(b.i); // Refers to B.i; prints 2. System.out.println(b.f()); // Refers to B.f(); prints -2. A a = (A) b; // Cast b to an instance of class A. System.out.println(a.i); // Now refers to A.i; prints 1; System.out.println(a.f()); // Still refers to B.f(); prints -2; } }
While this difference between method overriding and variable shadowing may seem surprising at first, a little thought makes the purpose clear. Suppose we have a bunch of Circle and Ellipse (a subclass of Circle) objects that we are manipulating. To keep track of the circles and ellipses, we store them in an array of type Circle[], casting all the Ellipse objects to Circle objects before we store them. Then, when we loop through the elements of this array, we dont have to know or care whether the element is actually a Circle or an Ellipse. What we do care very much about, however, is that the correct value is computed when we invoke the area() method of any element of the array. That is, we dont want to use the formula for the area of a circle when the object is actually an ellipse!
Seen in this context, it is not surprising at all that method overriding is handled differently by Java than variable shadowing.
14.3.7.2. final Methods
If a method is declared final, it means that the method declaration is the final onethat it cannot be overridden. static methods and private methods (which we havent learned about yet) cannot be overridden either, nor can the methods of a final class. If a method cannot be overridden, the compiler may perform certain optimizations on it, as well see below.
14.3.7.3. Dynamic Method Lookup
If we have an array of Circle and Ellipse objects, how does the compiler know to call the Circle area() method or the Ellipse area() method for any given item in the array? The compiler does not know this; it cant. The compiler knows that it does not know, however, and produces code that uses dynamic method lookup at runtime. When the interpreter runs the code, it looks up the appropriate area() method to call for each of the objects. That is, when the interpreter interprets the expression s.area(), it dynamically looks for an area() method associated with the particular object referred to by the variable s. It does not simply use the area() method that is statically associated with the type of the variable s.22
22C++ programmers should note that dynamic method lookup is what C++ does for virtual functions. An important difference between Java and C++ is that Java does not have a virtual keyword; methods in Java are virtual by default.
Dynamic method lookup is fast, but it is not as fast as invoking a method directly. Fortunately, there are a number of cases in which Java does not need to use dynamic method lookup. static methods cannot be overridden, so they are always invoked directly. private methods (which we havent learned about yet) are not inherited by subclasses and so cannot be overridden by subclasses; this means the Java compiler can safely invoke them without dynamic method lookup as well. final methods are invoked directly for the same reason: They cannot be overridden. Finally, when a method of a final class is invoked through an instance of the class, then it, too, can be invoked without the overhead of dynamic lookup. These static, final, and private methods that can be invoked directly are also candidates for inliningi.e., if the methods are short, the compiler may simply insert the method body into the code rather than inserting a call to the method.
Previous | Table of Contents | Next |