Java Reference
In-Depth Information
code is bound at runtime because m1() is an instance method. The compiler does not decide which m1() method's
code will be executed for c.m1(10) . Keep in mind that the compiler's decision is solely based on its knowledge
about class C1 . When c.m1(10) is compiled, the compiler does not know (or care) about the existence of any other
class, for example, C2 . You can see what code is generated for c.m1(10) method call by the Java compiler. You need to
use the javap command line utility with a -c option to disassemble the compiled code as follows. You need to pass
the fully qualified name of the class to the javap command.
javap -c your-fully-qualified-class-name
For the above code snippet that contains the c.m1(10) call, the javap will print instructions that are generated by
the compiler. I have shown only one instruction:
12: invokevirtual #14; // Method com/jdojo/inheritance /C1.m1:(D)V
The invokevirtual instruction is used to denote a call to an instance method that will use late binding. The #14
(it may be different for you) indicates the method table entry number, which is the entry for the C1.m1(D)V method.
The syntax is a little cryptic for you. The character D denotes double , which is the parameter type and V denotes void ,
which is the return type of the method m1() .
At runtime, when the JVM attempts to run c.m1(10) , it uses the late binding mechanism to find the method code
that it will execute. Note that the JVM will search for m(D)V method signature, which is the compiler syntax for void
m1(double) . It starts the search by looking at the runtime type of c , which is class C2 . Class C2 does not have a method
named m1 , which accepts a parameter of type double . The search moves up in the class hierarchy to class C1 . The JVM
finds the method in class C1 and it executed it. This is the reason that you got the output that indicates that m1(double
num) in the class C1 is called for c.m1(10) .
Such mistakes are very difficult to hunt down. Java 5 lets you avoid such mistakes by using the @Override annotation.
Please refer to Chapter 1 on annotations in the topic Beginning Java Language Features (ISBN 978-1-4302-6658-7) for
more details. The annotation has compiler support. The compiler will make sure a method that is annotated with the
@Override annotation really overrides a method in its superclass. Otherwise, it will generate an error. Using the @Override
annotation is easy. Just add it to the method declaration anywhere before the method's return type. The following code
for class C2 uses @Override annotation for the m1() method:
public class C2 extends C1 {
@Override
public void m1(int num) {
System.out.println("Inside C2.m1(): " + num);
}
}
When you compile the above code for class C2 , the compiler will generate an error stating that the method m1()
in class C2 does not override any method in its superclass. Using the @Override annotation with a method that is
supposed to override a superclass method saves you a lot of debugging time. Note that the @Override annotation does
not change the way method overriding works. It is used as an indicator to the compiler that it needs to make sure the
method really overrides the method of its superclass.
Is-a, has-a, and part-of Relationships
A software application, which is designed based on object-oriented paradigm, consists of interacting objects.
Objects of one class may be related to objects of another class in some ways. Is-a, has-a, and part-of are the three
most commonly used relationships that exist between objects of two classes. I have already discussed that an is-a
relationship is modeled using inheritance between two classes. For example, the relationship “A part-time manager
is-a manager” is modeled by inheriting PartTimeManager class from the Manager class.
 
Search WWH ::




Custom Search