Forgetting the object type
Music.java 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, youd 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: Feedback
//: c07:music:Music2.java
// Overloading instead of upcasting.
package c07.music;
import com.bruceeckel.simpletest.*;
class Stringed extends Instrument {
public void play(Note n) {
System.out.println("Stringed.play() " + n);
}
}
class Brass extends Instrument {
public void play(Note n) {
System.out.println("Brass.play() " + n);
}
}
public class Music2 {
private static Test monitor = new Test();
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);
monitor.expect(new String[] {
"Wind.play() Middle C",
"Stringed.play() Middle C",
"Brass.play() Middle C"
});
}
} ///:~This works, but theres 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, youve got a lot of work to do. Add the fact that the compiler wont give you any error messages if you forget to overload one of your methods and the whole process of working with types becomes unmanageable. Feedback
Wouldnt 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, wouldnt it be nice if you could forget that there are derived classes, and write your code to talk only to the base class? Feedback
Thats 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. Feedback
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 doesnt 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 cant. To get a deeper understanding of the issue, its helpful to examine the subject of binding. Feedback
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), its 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 thats early binding. Feedback
The confusing part of the preceding program revolves around early binding, because the compiler cannot know the correct method to call when it has only an Instrument reference. Feedback
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 doesnt 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. Feedback
All method binding in Java uses late binding unless the method is static or final (private methods are implicitly final). This means that ordinarily you dont need to make any decisions about whether late binding will occurit happens automatically. Feedback
Why would you declare a method final? As noted in the last chapter, it prevents anyone from overriding that method. Perhaps more important, it effectively turns off dynamic binding, or rather it tells the compiler that dynamic binding isnt necessary. This allows the compiler to generate slightly more efficient code for final method calls. However, in most cases it wont make any overall performance difference in your program, so its best to only use final as a design decision, and not as an attempt to improve performance. Feedback



RSS feed Java FAQ News