Chapter 7 Multi-shapes "For object-oriented programming languages, multiplexity is the third most basic feature (the first two are data abstraction and inheritance." "Polymorphism) will be from another angle The interface is separated from a specific implementation detail, that is, "what" is separated from "What to do". With the concept of polymorphism, the organization and readability can be improved. In addition, It can also create "easy to expand" procedures. Whether in the creation of the project, they can easily "growth" when needed to join new features. By merged various features and behaviors, packaging technology can create new Data type. By hidden to specific details, the interface can be separated from the implementation details, making all details "(private). This organization makes those programming background people feel comfortable. But more The shape involves the decomposition of "type". Through the study of the previous chapter, everyone knows that one object can be treated as its own type or its own basic type. This ability is very important because Multiple types (derived from the same basic type) can be treated as the same type. And only one code is required, you can do the same processing for all different types. Use with versatile methods, one Types can separate themselves from another similar type, as long as they are derived from the same basic type. This distinguishing is achieved by various methods in behavior, and can be implemented For those methods, in this chapter, everyone should learn about the problem of vectors from shallow to deeply (also called dynamic binding, postpone binding or running binding). At the same time, some simple examples, All of these are all stripped, only the code related to the versions. 7.1 Tracking in Chapter 6, everyone knows that an object can be used as its own type, or as a basic type of it Object use. A object handle is obtained, and it is called "traceable" as the behavior of the basic type handle - because the painting of the inherited tree is the top right. But this will also encounter a problem. As shown in the following example (if you have trouble, please refer to Chapter 3 3.1.2 "Assignment":
//: Music.java
// Inheritance & Upcasting
Package C07;
Class Note {
Private int value;
Private note (int val) {value = val;}
Public Static Final Note
Middlec = new Note (0),
Csharp = New Note (1),
cflat = 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 {
//Refine Interface Method:
Public void play (Note N) {
System.out.println ("Wind.Play ()");
}
}
Public class music {
Public Static Void Tune (Instrument i) {
// ...
I.Play (Note.Middlec);
}
Public static void main (String [] args) {
Wind flute = new window (); tune (flute); // upcasting
}
} ///: ~
Among them, Music.Tune () receives an Instrument handle while also receiving everything from Instrument. This happens when a Wind handle is passed to Tune (). There is no need to shape it. This is acceptable; the interface in Instrument must exist in Wind because Wind is inherited from Instrument. From Wind to Instrument, you may "reduce" that interface, but it is impossible to make it smaller than the full interface of Instrument.
7.1.1 Why is you going back?
This program seems to be a bit strange. Why should everyone have interested in forget the type of object? When you travel back, you may have doubts in this regard. And if tune () simply obtains a Wind handle, use it as its own own variable, it seems to be more simple and intuitive. But pay attention to: If you do it, you need to write a new Tune () for each type of Instrument in the system. Suppose it is in the previous inference, add StrUns and BRASS (Copper Tubes):
//: Music2.java
// overloading instead of upcasting
Class note2 {
Private int value;
Private Note2 (int value = val;)
Public Static Final Note2
Middlec = new note2 (0),
Csharp = new note2 (1),
CFLAT = New Note2 (2);
} // etc.
Class Instrument2 {
Public void play (Note2 N) {
System.out.println ("Instrument2.Play ()");
}
}
Class wind2 extends instruction2 {
Public void play (Note2 N) {
System.out.println ("Wind2.Play ()");
}
}
Class stringed2 extends instructionent2 {
Public void play (Note2 N) {
System.out.println ("Stringed2.Play ()");
}
}
Class Brass2 Extends Instrument2 {
Public void play (Note2 N) {
System.out.println ("BRASS2.PLAY ()");
}
}
Public class music2 {
Public static void tune (wind2 i) {
I.Play (Note2.Middlec);
}
Public static void tune (stringed2 i) {
I.Play (Note2.Middlec);
}
Public Static Void Tune (BRASS2 I) {
I.Play (Note2.Middlec);
}
Public static void main (String [] args) {
Wind2 flute = new window2 ();
Stringed2 Violin = new stringed2 ();
BRASS2 FRENCHORN = New Brass2 ();
Tune (flute); // no upcastingTune (violin);
Tune (French ";
}
} ///: ~
This is to do it, but there is a great drawback: you must prepare a closely related method for each new Instrument2 class. This means that much more programmed amounts are required for the first time. In the future, if you want to add a new method like Tune () or add a new type to INSTRUMENT, you still need a lot of encoding. In addition, even if you forget to overload your own method, the compiler will not prompt any errors. In this way, the entire operation process of the type is extremely difficult to manage, and there is a risk of out-of control.
However, if only one method is written, use the basic class as an argument or parameter, rather than using those specific derivatives, isn't it simple? That is, if we can derive the class, only let your code to deal with the basic class, then the amount of work will be difficult to estimate.
This is the place where "versatile" is characterized. However, most programmers (especially for programming background) are still somewhat raw.
7.2 In-depth understanding
For Music.java's difficulties, it can be experienced by running procedures. The output is Wind.Play (). This is of course the output of our hopes, but it seems that it does not want to act according to our hopes. Please observe the tune () method:
Public Static Void Tune (Instrument i) {
// ...
I.Play (Note.Middlec);
}
It receives the INSTRUMENT handle. So in this case, how can the compiler know that the INSTRUMENT handle points to a Wind, not a BRASS or STRINGED? The compiler is not known. In order to understand this problem, we need to explore the theme of "binding".
7.2.1 Binding of the method call
Calling a method calls the same method body to connect together is called "binding". If the binding is performed before the program runs (by the compiler and linker, if any), it is called "early binding". Everyone I have never heard of this term because it is impossible in any programming language. The C compiler has only one way to call, that is, "early binding".
The most confusing place in the above program is related to early binding, because in the premise of only one Instrument handle, the compiler does not know which method of the specific call.
The method of solving is "post-binding", which means that the binding is performed during operation, based on the type of object. The post-binding is also called "dynamic binding" or "running binding". If a language implements a later binding, some mechanisms must be provided, and the type of object can be determined during operation, and the appropriate method is called separately. That is, the compiler still does not know the type of object, but the method call mechanism can go to investigate, find the correct method body. Different languages have different methods of implementation of later bindings. But we can at least think that they must set some special types of information in the object.
All methods binding in Java use post-binding techniques unless a method has been declared into Final. This means that we usually do not have to decide whether it should be binding - it is automatically occurred.
Why do you declare a way to Final? As the previous chapter pointed out, it prevents others from overwriting that method. But maybe more importantly, it can effectively "close" dynamic binding, or tell the compiler that does not need to perform dynamic binding. In this way, the compiler can call the FINAL method to generate higher code.
7.2.2 Creating the right behavior
All methods of knowing Java bindings are backed by later binding, you can write your own code accordingly, communicate with the base class. At this point, all derivative classes ensure that it can work properly with the same code. Or for another method, we can "send a message to an object, let the object to determine what to do." In the object-oriented program, there is a classic "shape" example. Because it is easy to manifest by visualization, it is often used to explain the problem. But unfortunately, it may mislead beginners think that OOP is just designed for graphical programming, this understanding is of course wrong.