⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 chap07.htm

📁 java书籍《thinking in java》
💻 HTM
📖 第 1 页 / 共 5 页
字号:
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The main class <B>Shapes</B> contains a
<B>static</B> method <B>randShape(&#160;)</B> that produces a reference to a
randomly-selected <B>Shape</B> object each time you call it. Note that the
upcasting happens in each of the <B>return</B> statements, which take a
reference to a <B>Circle</B>, <B>Square</B>, or <B>Triangle</B> and sends it out
of the method as the return type, <B>Shape</B>. 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 <B>Shape</B> reference.

</backtalk:display>
[&nbsp;<a href='http://www.mindview.net/backtalk/CommentServlet?ID=TIJ3_CHAPTER7_I19' 
  target="_blank">Add&nbsp;Comment</a>&nbsp;]

<backtalk:display ID=TIJ3_CHAPTER7_I20>
</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia"><B>main(&#160;)</B> contains an array of
<B>Shape</B> references filled through calls to <B>randShape(&#160;)</B>. At
this point you know you have <B>Shape</B>s, but you don&#8217;t know anything
more specific than that (and neither does the compiler). However, when you step
through this array and call <B>draw(&#160;)</B> for each one, the correct
type-specific behavior magically occurs, as you can see from one output
example:</FONT><BR></P></DIV>

<BLOCKQUOTE><FONT SIZE = "+1"><PRE>Circle.draw()
Triangle.draw()
Circle.draw()
Circle.draw()
Circle.draw()
Square.draw()
Triangle.draw()
Square.draw()
Square.draw()</PRE></FONT></BLOCKQUOTE>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">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 <B>draw(&#160;)</B> are made through dynamic
binding.

</backtalk:display>
[&nbsp;<a href='http://www.mindview.net/backtalk/CommentServlet?ID=TIJ3_CHAPTER7_I20' 
  target="_blank">Add&nbsp;Comment</a>&nbsp;]

<backtalk:display ID=TIJ3_CHAPTER7_I21>
</FONT><A NAME="_Toc375545332"></A><A NAME="_Toc481064630"></A><BR></P></DIV>
<A NAME="Heading240"></A><FONT FACE = "Verdana"><H3 ALIGN="LEFT">
Extensibility</H3></FONT>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Now let&#8217;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 <B>tune(&#160;)</B> method. In a
well-designed OOP program, most or all of your methods will follow the model of
<B>tune(&#160;)</B> and communicate only with the base-class
interface<A NAME="Index691"></A><A NAME="Index692"></A>. Such a program is
<I>extensible<A NAME="Index693"></A></I> 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.

</backtalk:display>
[&nbsp;<a href='http://www.mindview.net/backtalk/CommentServlet?ID=TIJ3_CHAPTER7_I21' 
  target="_blank">Add&nbsp;Comment</a>&nbsp;]

<backtalk:display ID=TIJ3_CHAPTER7_I22>
</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Consider what happens if you take the
instrument example and add more methods in the base class and a number of new
classes. Here&#8217;s the diagram:</FONT><BR></P></DIV>
<DIV ALIGN="CENTER"><FONT FACE="Georgia"><IMG SRC="TIJ216.gif"></FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">All these new classes work correctly with
the old, unchanged <B>tune(&#160;)</B> method. Even if <B>tune(&#160;)</B> is in
a separate file and new methods are added to the interface of <B>Instrument</B>,
<B>tune(&#160;)</B> works correctly without recompilation. Here is the
implementation of the above diagram:</FONT><BR></P></DIV>

<BLOCKQUOTE><FONT SIZE = "+1"><PRE><font color=#009900>//: c07:music3:Music3.java</font>
<font color=#009900>// An extensible program.</font>
<font color=#0000ff>import</font> java.util.*;

<font color=#0000ff>class</font> Instrument {
  <font color=#0000ff>public</font> <font color=#0000ff>void</font> play() {
    System.out.println(<font color=#004488>"Instrument.play()"</font>);
  }
  <font color=#0000ff>public</font> String what() {
    <font color=#0000ff>return</font> <font color=#004488>"Instrument"</font>;
  }
  <font color=#0000ff>public</font> <font color=#0000ff>void</font> adjust() {}
}

<font color=#0000ff>class</font> Wind <font color=#0000ff>extends</font> Instrument {
  <font color=#0000ff>public</font> <font color=#0000ff>void</font> play() {
    System.out.println(<font color=#004488>"Wind.play()"</font>);
  }
  <font color=#0000ff>public</font> String what() { <font color=#0000ff>return</font> <font color=#004488>"Wind"</font>; }
  <font color=#0000ff>public</font> <font color=#0000ff>void</font> adjust() {}
}

<font color=#0000ff>class</font> Percussion <font color=#0000ff>extends</font> Instrument {
  <font color=#0000ff>public</font> <font color=#0000ff>void</font> play() {
    System.out.println(<font color=#004488>"Percussion.play()"</font>);
  }
  <font color=#0000ff>public</font> String what() { <font color=#0000ff>return</font> <font color=#004488>"Percussion"</font>; }
  <font color=#0000ff>public</font> <font color=#0000ff>void</font> adjust() {}
}

<font color=#0000ff>class</font> Stringed <font color=#0000ff>extends</font> Instrument {
  <font color=#0000ff>public</font> <font color=#0000ff>void</font> play() {
    System.out.println(<font color=#004488>"Stringed.play()"</font>);
  }
  <font color=#0000ff>public</font> String what() { <font color=#0000ff>return</font> <font color=#004488>"Stringed"</font>; }
  <font color=#0000ff>public</font> <font color=#0000ff>void</font> adjust() {}
}

<font color=#0000ff>class</font> Brass <font color=#0000ff>extends</font> Wind {
  <font color=#0000ff>public</font> <font color=#0000ff>void</font> play() {
    System.out.println(<font color=#004488>"Brass.play()"</font>);
  }
  <font color=#0000ff>public</font> <font color=#0000ff>void</font> adjust() {
    System.out.println(<font color=#004488>"Brass.adjust()"</font>);
  }
}

<font color=#0000ff>class</font> Woodwind <font color=#0000ff>extends</font> Wind {
  <font color=#0000ff>public</font> <font color=#0000ff>void</font> play() {
    System.out.println(<font color=#004488>"Woodwind.play()"</font>);
  }
  <font color=#0000ff>public</font> String what() { <font color=#0000ff>return</font> <font color=#004488>"Woodwind"</font>; }
}

<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(Instrument i) {
    <font color=#009900>// ...</font>
    i.play();
  }
  <font color=#0000ff>static</font> <font color=#0000ff>void</font> tuneAll(Instrument[] e) {
    <font color=#0000ff>for</font>(<font color=#0000ff>int</font> i = 0; i &lt; e.length; i++)
      tune(e[i]);
  }
  <font color=#0000ff>public</font> <font color=#0000ff>static</font> <font color=#0000ff>void</font> main(String[] args) {
    Instrument[] orchestra = <font color=#0000ff>new</font> Instrument[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> Wind();
    orchestra[i++] = <font color=#0000ff>new</font> Percussion();
    orchestra[i++] = <font color=#0000ff>new</font> Stringed();
    orchestra[i++] = <font color=#0000ff>new</font> Brass();
    orchestra[i++] = <font color=#0000ff>new</font> Woodwind();
    tuneAll(orchestra);
  }
} <font color=#009900>///:~</font></PRE></FONT></BLOCKQUOTE>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The new methods are <B>what(&#160;)</B>,
which returns a <B>String</B> reference with a description of the class, and
<B>adjust(&#160;)</B>, which provides some way to adjust each instrument.

</backtalk:display>
[&nbsp;<a href='http://www.mindview.net/backtalk/CommentServlet?ID=TIJ3_CHAPTER7_I22' 
  target="_blank">Add&nbsp;Comment</a>&nbsp;]

<backtalk:display ID=TIJ3_CHAPTER7_I23>
</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">In <B>main(&#160;)</B>, when you place
something inside the <B>Instrument </B>array you automatically upcast to
<B>Instrument</B>. 
</backtalk:display>
[&nbsp;<a href='http://www.mindview.net/backtalk/CommentServlet?ID=TIJ3_CHAPTER7_I23' 
  target="_blank">Add&nbsp;Comment</a>&nbsp;]

<backtalk:display ID=TIJ3_CHAPTER7_I24>
</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">You can see that the <B>tune(&#160;)</B>
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&#8217;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 &#8220;separate the things
that change from the things that stay the same.&#8221;

</backtalk:display>
[&nbsp;<a href='http://www.mindview.net/backtalk/CommentServlet?ID=TIJ3_CHAPTER7_I24' 
  target="_blank">Add&nbsp;Comment</a>&nbsp;]

<backtalk:display ID=TIJ3_CHAPTER7_I25>
</FONT><A NAME="_Toc375545333"></A><A NAME="_Toc481064631"></A><BR></P></DIV>
<A NAME="Heading241"></A><FONT FACE = "Verdana"><H2 ALIGN="LEFT">
Overriding vs.
overloading<BR><A NAME="Index694"></A><A NAME="Index695"></A></H2></FONT>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Let&#8217;s take a different look at the
first example in this chapter. In the following program, the interface of the
method <B>play(&#160;)</B> is changed in the process of overriding it, which
means that you haven&#8217;t <I>overridden</I> the method, but instead
<I>overloaded </I>it. The compiler allows you to overload methods so it gives no
complaint. But the behavior is probably not what you want. Here&#8217;s the
example:</FONT><BR></P></DIV>

<BLOCKQUOTE><FONT SIZE = "+1"><PRE><font color=#009900>//: c07:WindError.java </font>
<font color=#009900>// Accidentally changing the interface.</font>

<font color=#0000ff>class</font> NoteX {
  <font color=#0000ff>public</font> <font color=#0000ff>static</font> <font color=#0000ff>final</font> <font color=#0000ff>int</font>
    MIDDLE_C = 0, C_SHARP = 1, C_FLAT = 2;
}

<font color=#0000ff>class</font> InstrumentX {
  <font color=#0000ff>public</font> <font color=#0000ff>void</font> play(<font color=#0000ff>int</font> NoteX) {
    System.out.println(<font color=#004488>"InstrumentX.play()"</font>);
  }
}

<font color=#0000ff>class</font> WindX <font color=#0000ff>extends</font> InstrumentX {
  <font color=#009900>// OOPS! Changes the method interface:</font>
  <font color=#0000ff>public</font> <font color=#0000ff>void</font> play(NoteX n) {
    System.out.println(<font color=#004488>"WindX.play(NoteX n)"</font>);
  }
}

<font color=#0000ff>public</font> <font color=#0000ff>class</font> WindError {
  <font color=#0000ff>public</font> <font color=#0000ff>static</font> <font color=#0000ff>void</font> tune(InstrumentX i) {
    <font color=#009900>// ...</font>
    i.play(NoteX.MIDDLE_C);
  }
  <font color=#0000ff>public</font> <font color=#0000ff>static</font> <font color=#0000ff>void</font> main(String[] args) {
    WindX flute = <font color=#0000ff>new</font> WindX();
    tune(flute); <font color=#009900>// Not the desired behavior!</font>
  }
} <font color=#009900>///:~</font></PRE></FONT></BLOCKQUOTE>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">There&#8217;s another confusing aspect
thrown in here. In <B>InstrumentX</B>, the <B>play(&#160;)</B> method takes an
<B>int</B> that has the identifier <B>NoteX</B>. That is, even though
<B>NoteX</B> is a class name, it can also be used as an identifier without
complaint. But in <B>WindX</B>, <B>play(&#160;)</B> takes a <B>NoteX</B>
reference that has an identifier <B>n.</B> (Although you could even say
<B>play(NoteX NoteX)</B> without an error.) Thus it appears that the programmer
intended to override <B>play(&#160;)</B> but mistyped the method a bit. The
compiler, however, assumed that an overload and not an override was intended.
Note that if you follow the standard Java naming convention, the argument
identifier would be <B>noteX</B> (lowercase &#8216;n&#8217;), which would
distinguish it from the class name.

</backtalk:display>
[&nbsp;<a href='http://www.mindview.net/backtalk/CommentServlet?ID=TIJ3_CHAPTER7_I25' 
  target="_blank">Add&nbsp;Comment</a>&nbsp;]

<backtalk:display ID=TIJ3_CHAPTER7_I26>
</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">In <B>tune</B>, the <B>InstrumentX</B>
<B>i</B> is sent the <B>play(&#160;)</B> message, with one of
<B>NoteX</B>&#8217;s members (<B>MIDDLE_C</B>) as an argument. Since
<B>NoteX</B> contains <B>int</B> definitions, this means that the <B>int</B>
version of the now-overloaded <B>play(&#160;)</B> method is called, and since
that has <I>not</I> been overridden the base-class version is used.

</backtalk:display>
[&nbsp;<a href='http://www.mindview.net/backtalk/CommentServlet?ID=TIJ3_CHAPTER7_I26' 
  target="_blank">Add&nbsp;Comment</a>&nbsp;]

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -