Suppose you call one of the base-class methods (that have been overridden in the derived classes): s.draw(); Again, you might expect that Shape’s draw() is called because this is, after all, a Shape reference—so how could the compiler know to do anything else? And yet the proper Circle.draw() is called because of late binding (polymorphism).
The base class Shape establishes the common interface to anything inherited from Shape—that is, all shapes can be drawn and erased. The derived classes override these definitions to provide unique behavior for each specific type of shape.
RandomShapeGenerator is a kind of “factory” that produces a reference to a randomly-selected Shape object each time you call its next() method. Note that the upcasting happens in the return statements, each of which takes a reference to a Circle, Square, or Triangle and sends it out of next() as the return type, Shape. So whenever you call next( ), you never get a chance to see what specific type it is, since you always get back a plain Shape reference.
main() contains an array of Shape references filled through calls to RandomShapeGenerator.next(). At this point you know you have Shapes, but you don’t know anything more specific than that (and neither does the compiler). However, when you step through this array and call draw() for each one, the correct type-specific behavior magically occurs, as you can see from the output when you run the program.
The point of choosing the shapes randomly is to drive home the understanding that the compiler can have no special knowledge that allows it to make the correct calls at compile time. All the calls to draw() must be made through dynamic binding.
Now let’s return to the musical instrument example. Because of polymorphism, you can add as many new types as you want to the system without changing the tune() method. In a well-designed OOP program, most or all of your methods will follow the model of tune() and communicate only with the base-class interface. Such a program is extensible because you can add new functionality by inheriting new data types from the common base class. The methods that manipulate the base-class interface will not need to be changed at all to accommodate the new classes.
Consider what happens if you take the instrument example and add more methods in the base class and a number of new classes. Here’s the diagram:
Fig. 2 The Realationship of Class
All these new classes work correctly with the old, unchanged tune() method. Even if tune() is in a separate file and new methods are added to the interface of Instrument, tune() will still work correctly, even without recompiling it.
The new methods are what(), which returns a String reference with a description of the class, and adjust(), which provides some way to adjust each instrument.
In main(), when you place something inside the orchestra array, you automatically upcast to Instrument.
You can see that the tune() method is blissfully ignorant of all the code changes that have happened around it, and yet it works correctly. This is exactly what polymorphism is supposed to provide. Changes in your code don’t cause damage to parts of the program that should not be affected. Put another way, polymorphism is an important technique for the programmer to “separate the things that change from the things that stay the same,.”
Pitfall: “overriding” private methods.
You might reasonably expect the output to be “public f()”, but a private method is automatically final, and is also hidden from the derived class. So Derived’s f() in this case is a brand new method; it’s not even overloaded, since the base-class version of f() isn’t visible in Derived.
The result of this is that only non-private methods may be overridden, but you should watch out for the appearance of overriding private methods, which generates no compiler warnings, but doesn’t do what you might expect. To be clear, you should use a different name from a private base-class method in your derived class. The hierarchy of constructor calls brings up an interesting dilemma. What happens if you’re inside a constructor and you call a dynamically-bound method of the object being constructed? Inside an ordinary method, you can imagine what will happen: The dynamically-bound call is resolved at run time, because the object cannot know whether it belongs to the class that the method is in or some class derived from it. For consistency, you might think this is what should happen inside constructors.
- 上一篇:电子商务英文文献和中文翻译
- 下一篇:搅拌釜内混合液体的分离涡模拟英文文献和中文翻译
-
-
-
-
-
-
-
现代简约美式风格在室内家装中的运用
高警觉工作人群的元情绪...
NFC协议物理层的软件实现+文献综述
上市公司股权结构对经营绩效的影响研究
浅析中国古代宗法制度
巴金《激流三部曲》高觉新的悲剧命运
C++最短路径算法研究和程序设计
g-C3N4光催化剂的制备和光催化性能研究
江苏省某高中学生体质现状的调查研究
中国传统元素在游戏角色...