📄 tij0077.html
字号:
s[i] = randShape();
<font color="#009900">// Make polymorphic method calls:</font>
<font color="#0000ff">for</font>(<font color="#0000ff">int</font> i = 0; i < s.length; i++)
s[i].draw();
}
} <font color="#009900">///:~ </PRE></font></font><DIV ALIGN=LEFT><P></DIV><DIV ALIGN=LEFT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">The
base class
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>Shape</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
establishes the common interface to anything inherited from
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>Shape</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
– 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.
</FONT><P></DIV><DIV ALIGN=LEFT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">The
main class
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>Shapes</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
contains a
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>static</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
method
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>randShape( )</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
that produces a handle to a randomly-selected
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>Shape</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
object each time you call it. Note that the upcasting happens in each of the
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>return</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
statements, which take a handle to a
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>Circle</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">,
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>Square</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">,
or
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>Triangle</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
and send it out of the method as the return type,
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>Shape</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">.
So whenever you call this method you never get a chance to see what specific
type it is, since you always get back a plain
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>Shape</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
handle.
</FONT><P></DIV><DIV ALIGN=LEFT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>main( )</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
contains an array of
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>Shape</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
handles filled through calls to
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>randShape( )</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">.
At this point you know you have
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>Shape</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">s,
but you don’t know anything more specific than that (and neither does the
compiler). However, when you step through this array and call
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>draw( )</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
for each one, the correct type-specific behavior magically occurs, as you can
see from one output example:
</FONT><P></DIV>
<font color="#990000"><PRE>Circle.draw()
Triangle.draw()
Circle.draw()
Circle.draw()
Circle.draw()
Square.draw()
Triangle.draw()
Square.draw()
Square.draw()</PRE></font><DIV ALIGN=LEFT><P></DIV><DIV ALIGN=LEFT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">Of
course, since the shapes are all chosen randomly each time, your runs will have
different results. 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
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>draw( )</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
are made through dynamic binding.
</FONT><a name="_Toc375545332"></a><a name="_Toc408018535"></a><P></DIV>
<A NAME="Heading213"></A><H3 ALIGN=LEFT>
Extensibility</H3>
<DIV ALIGN=LEFT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">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
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>tune( )</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
method. In a well-designed OOP program, most or all of your methods will follow
the model of
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>tune( )</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
and communicate only with the base-class interface<A NAME="Index574"></A><A NAME="Index575"></A>.
Such a program is
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><I>extensible<A NAME="Index576"></A></I></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
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.
</FONT><P></DIV><DIV ALIGN=LEFT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">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:
</FONT><P></DIV><DIV ALIGN=LEFT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">All
these new classes work correctly with the old, unchanged
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>tune( )</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
method. Even if
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>tune( )</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
is in a separate file and new methods are added to the interface of
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>Instrument</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">,
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>tune( )</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
works correctly without recompilation. Here is the implementation of the above
diagram:
</FONT><P></DIV>
<font color="#990000"><PRE><font color="#009900">//: Music3.java</font>
<font color="#009900">// An extensible program</font>
<font color="#0000ff">import</font> java.util.*;
<font color="#0000ff">class</font> Instrument3 {
<font color="#0000ff">public</font> <font color="#0000ff">void</font> play() {
System.out.println("Instrument3.play()");
}
<font color="#0000ff">public</font> String what() {
<font color="#0000ff">return</font> "Instrument3";
}
<font color="#0000ff">public</font> <font color="#0000ff">void</font> adjust() {}
}
<font color="#0000ff">class</font> Wind3 <font color="#0000ff">extends</font> Instrument3 {
<font color="#0000ff">public</font> <font color="#0000ff">void</font> play() {
System.out.println("Wind3.play()");
}
<font color="#0000ff">public</font> String what() { <font color="#0000ff">return</font> "Wind3"; }
<font color="#0000ff">public</font> <font color="#0000ff">void</font> adjust() {}
}
<font color="#0000ff">class</font> Percussion3 <font color="#0000ff">extends</font> Instrument3 {
<font color="#0000ff">public</font> <font color="#0000ff">void</font> play() {
System.out.println("Percussion3.play()");
}
<font color="#0000ff">public</font> String what() { <font color="#0000ff">return</font> "Percussion3"; }
<font color="#0000ff">public</font> <font color="#0000ff">void</font> adjust() {}
}
<font color="#0000ff">class</font> Stringed3 <font color="#0000ff">extends</font> Instrument3 {
<font color="#0000ff">public</font> <font color="#0000ff">void</font> play() {
System.out.println("Stringed3.play()");
}
<font color="#0000ff">public</font> String what() { <font color="#0000ff">return</font> "Stringed3"; }
<font color="#0000ff">public</font> <font color="#0000ff">void</font> adjust() {}
}
<font color="#0000ff">class</font> Brass3 <font color="#0000ff">extends</font> Wind3 {
<font color="#0000ff">public</font> <font color="#0000ff">void</font> play() {
System.out.println("Brass3.play()");
}
<font color="#0000ff">public</font> <font color="#0000ff">void</font> adjust() {
System.out.println("Brass3.adjust()");
}
}
<font color="#0000ff">class</font> Woodwind3 <font color="#0000ff">extends</font> Wind3 {
<font color="#0000ff">public</font> <font color="#0000ff">void</font> play() {
System.out.println("Woodwind3.play()");
}
<font color="#0000ff">public</font> String what() { <font color="#0000ff">return</font> "Woodwind3"; }
}
<font color="#0000ff">public</font> <font color="#0000ff">class</font> Music3 {
<font color="#009900">// Doesn't care about type, so new types</font>
<font color="#009900">// added to the system still work right:</font>
<font color="#0000ff">static</font> <font color="#0000ff">void</font> tune(Instrument3 i) {
<font color="#009900">// ...</font>
i.play();
}
<font color="#0000ff">static</font> <font color="#0000ff">void</font> tuneAll(Instrument3[] e) {
<font color="#0000ff">for</font>(<font color="#0000ff">int</font> i = 0; i < e.length; i++)
tune(e[i]);
}
<font color="#0000ff">public</font> <font color="#0000ff">static</font> <font color="#0000ff">void</font> main(String[] args) {
Instrument3[] orchestra = <font color="#0000ff">new</font> Instrument3[5];
<font color="#0000ff">int</font> i = 0;
<font color="#009900">// Upcasting during addition to the array:</font>
orchestra[i++] = <font color="#0000ff">new</font> Wind3();
orchestra[i++] = <font color="#0000ff">new</font> Percussion3();
orchestra[i++] = <font color="#0000ff">new</font> Stringed3();
orchestra[i++] = <font color="#0000ff">new</font> Brass3();
orchestra[i++] = <font color="#0000ff">new</font> Woodwind3();
tuneAll(orchestra);
}
} <font color="#009900">///:~ </PRE></font></font><DIV ALIGN=LEFT><P></DIV><DIV ALIGN=LEFT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">The
new methods are
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>what( )</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">,
which returns a
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>String</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
handle with a description of the class, and
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>adjust( )</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">,
which provides some way to adjust each instrument.
</FONT><P></DIV><DIV ALIGN=LEFT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">In
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>main( )</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">,
when you place something inside the
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>Instrument3
</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">array
you automatically upcast to
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>Instrument3</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">.</FONT><P></DIV><DIV ALIGN=LEFT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">You
can see that the
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>tune( )</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
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. Your code changes don’t cause damage to parts of the program
that should not be affected. Put another way, polymorphism is one of the most
important techniques that allow the programmer to “separate the things
that change from the things that stay the same.”
</FONT><a name="_Toc375545333"></a><a name="_Toc408018536"></a><P></DIV>
<div align="right">
<a href="tij_c.html">Contents</a> | <a href="tij0076.html">Prev</a> | <a href="tij0078.html">Next</a>
</div>
</body></html>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -