here is an excerpt from chapter 7 of "Thinking in java" which is available free from
www.bruceeckel.com Upcasting revisited
In Chapter 6 you saw how an object can be used as its own type or as an object of its base type. Taking an object reference and treating it as a reference to its base type is called upcasting, because of the way inheritance trees are drawn with the base class at the top.
You also saw a problem arise, which is embodied in the following:
//: c07:music:Music.java
// Inheritance & upcasting.
class Note {
private int value;
private Note(int val) { value = val; }
public static final Note
MIDDLE_C = new Note(0),
C_SHARP = new Note(1),
B_FLAT = new Note(2);
} // Etc.
class Instrument {
public void play(Note n) {
System.out.println("Instrument.play()");
}
}
// Wind objects are instruments
// because they have the same interface:
class Wind extends Instrument {
// Redefine interface method:
public void play(Note n) {
System.out.println("Wind.play()");
}
}
public class Music {
public static void tune(Instrument i) {
// ...
i.play(Note.MIDDLE_C);
}
public static void main(String[] args) {
Wind flute = new Wind();
tune(flute); // Upcasting
}
} ///:~
The method Music.tune( ) accepts an Instrument reference, but also anything derived from Instrument. In main( ), you can see this happening as a Wind reference is passed to tune( ), with no cast necessary. This is acceptable; the interface in Instrument must exist in Wind, because Wind is inherited from Instrument. Upcasting from Wind to Instrument may �narrow� that interface, but it cannot make it anything less than the full interface to Instrument.
Forgetting the object type
This program might seem strange to you. Why should anyone intentionally forget the type of an object? This is what happens when you upcast, and it seems like it could be much more straightforward if tune( ) simply takes a Wind reference as its argument. This brings up an essential point: If you did that, you�d need to write a new tune( ) for every type of Instrument in your system. Suppose we follow this reasoning and add Stringed and Brass instruments:
//: c07:music2:Music2.java
// Overloading instead of upcasting.
class Note {
private int value;
private Note(int val) { value = val; }
public static final Note
MIDDLE_C = new Note(0),
C_SHARP = new Note(1),
B_FLAT = new Note(2);
} // Etc.
class Instrument {
public void play(Note n) {
System.out.println("Instrument.play()");
}
}
class Wind extends Instrument {
public void play(Note n) {
System.out.println("Wind.play()");
}
}
class Stringed extends Instrument {
public void play(Note n) {
System.out.println("Stringed.play()");
}
}
class Brass extends Instrument {
public void play(Note n) {
System.out.println("Brass.play()");
}
}
public class Music2 {
public static void tune(Wind i) {
i.play(Note.MIDDLE_C);
}
public static void tune(Stringed i) {
i.play(Note.MIDDLE_C);
}
public static void tune(Brass i) {
i.play(Note.MIDDLE_C);
}
public static void main(String[] args) {
Wind flute = new Wind();
Stringed violin = new Stringed();
Brass frenchHorn = new Brass();
tune(flute); // No upcasting
tune(violin);
tune(frenchHorn);
}
} ///:~
This works, but there�s a major drawback: You must write type-specific methods for each new Instrument class you add. This means more programming in the first place, but it also means that if you want to add a new method like tune( ) or a new type of Instrument, you�ve got a lot of work to do. Add the fact that the compiler won�t give you any error messages if you forget to overload one of your methods and the whole process of working with types becomes unmanageable.
Wouldn�t it be much nicer if you could just write a single method that takes the base class as its argument, and not any of the specific derived classes? That is, wouldn�t it be nice if you could forget that there are derived classes, and write your code to talk only to the base class?
That�s exactly what
polymorphism allows you to do. However, most programmers who come from a procedural programming background have a bit of trouble with the way polymorphism works.
The twist
The difficulty with Music.java can be seen by running the program. The output is Wind.play( ). This is clearly the desired output, but it doesn�t seem to make sense that it would work that way. Look at the tune( ) method:
public static void tune(Instrument i) {
// ...
i.play(Note.MIDDLE_C);
}
It receives an Instrument reference. So how can the compiler possibly know that this Instrument reference points to a Wind in this case and not a Brass or Stringed? The compiler can�t. To get a deeper understanding of the issue, it�s helpful to examine the subject of binding.
Method-call binding
Connecting a method call to a method body is called binding. When binding is performed before the program is run (by the compiler and linker, if there is one), it�s called early binding. You might not have heard the term before because it has never been an option with procedural languages. C compilers have only one kind of method call, and that�s early binding.
The confusing part of the above program revolves around early binding because the compiler cannot know the correct method to call when it has only an Instrument reference.
The solution is called late binding, which means that the binding occurs at run-time based on the type of object. Late binding is also called dynamic binding or run-time binding. When a language implements late binding, there must be some mechanism to determine the type of the object at run-time and to call the appropriate method. That is, the compiler still doesn�t know the object type, but the method-call mechanism finds out and calls the correct method body. The late-binding mechanism varies from language to language, but you can imagine that some sort of type information must be installed in the objects.
All method binding in Java uses late binding unless a method has been declared final. This means that ordinarily you don�t need to make any decisions about whether late binding will occur�it happens automatically.