📄 chapter07.html
字号:
<font color=#0000ff>class</font> Instrument3 {
<font color=#0000ff>public</font> <font color=#0000ff>void</font> play() {
System.out.println(<font color=#004488>"Instrument3.play()"</font>);
}
<font color=#0000ff>public</font> String what() {
<font color=#0000ff>return</font> <font color=#004488>"Instrument3"</font>;
}
<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(<font color=#004488>"Wind3.play()"</font>);
}
<font color=#0000ff>public</font> String what() { <font color=#0000ff>return</font> <font color=#004488>"Wind3"</font>; }
<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(<font color=#004488>"Percussion3.play()"</font>);
}
<font color=#0000ff>public</font> String what() { <font color=#0000ff>return</font> <font color=#004488>"Percussion3"</font>; }
<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(<font color=#004488>"Stringed3.play()"</font>);
}
<font color=#0000ff>public</font> String what() { <font color=#0000ff>return</font> <font color=#004488>"Stringed3"</font>; }
<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(<font color=#004488>"Brass3.play()"</font>);
}
<font color=#0000ff>public</font> <font color=#0000ff>void</font> adjust() {
System.out.println(<font color=#004488>"Brass3.adjust()"</font>);
}
}
<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(<font color=#004488>"Woodwind3.play()"</font>);
}
<font color=#0000ff>public</font> String what() { <font color=#0000ff>return</font> <font color=#004488>"Woodwind3"</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(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>///:~</font></PRE></FONT></BLOCKQUOTE>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The new methods are
<B>what( )</B>, which returns a <B>String</B> handle with a description of
the class, and <B>adjust( )</B>, which provides some way to adjust each
instrument.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">In <B>main( )</B>, when you
place something inside the <B>Instrument3 </B>array you automatically upcast to
<B>Instrument3</B>.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">You can see that the
<B>tune( )</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’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><BR></P></DIV>
<A NAME="Heading213"></A><FONT FACE = "Verdana"><H2 ALIGN="LEFT">
Overriding vs.
overloading<BR><A NAME="Index577"></A><A NAME="Index578"></A></H2></FONT>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Let’s take a different look
at the first example in this chapter. In the following program, the interface of
the method <B>play( )</B> is changed in the process of overriding it, which
means that you haven’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’s the
example:</FONT><BR></P></DIV>
<BLOCKQUOTE><FONT SIZE = "+1"><PRE><font color=#009900>//: 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’s another confusing
aspect thrown in here. In <B>InstrumentX</B>, the <B>play( )</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( )</B> takes a <B>NoteX</B> handle
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( )</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> which would distinguish it from the class name.</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( )</B> message, with one of
<B>NoteX</B>’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( )</B> method is called, and since
that has <I>not</I> been overridden the base-class version is
used.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The output is:</FONT><BR></P></DIV>
<BLOCKQUOTE><FONT SIZE = "+1"><PRE>InstrumentX.play()</PRE></FONT></BLOCKQUOTE>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">This certainly doesn’t appear
to be a polymorphic method call. Once you understand what’s happening, you
can fix the problem fairly easily, but imagine how difficult it might be to find
the bug if it’s buried in a program of significant
size.</FONT><A NAME="_Toc375545334"></A><A NAME="_Toc408018537"></A><BR></P></DIV>
<A NAME="Heading214"></A><FONT FACE = "Verdana"><H2 ALIGN="LEFT">
Abstract classes <BR>and methods</H2></FONT>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">In all the instrument examples, the
methods in the base class <B>Instrument</B> were always “dummy”
methods. If these methods are ever called, you’ve done something wrong.
That’s because the intent of <B>Instrument</B> is to create a <I>common
interface</I> for all the classes derived from it.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The only reason to establish this
common interface<A NAME="Index579"></A><A NAME="Index580"></A> is so it can be
expressed differently for each different subtype. It establishes a basic form,
so you can say what’s in common with all the derived classes. Another way
of saying this is to call <B>Instrument</B> an <I>abstract base class</I>
<A NAME="Index581"></A><A NAME="Index582"></A><A NAME="Index583"></A>(or simply
an <I>abstract class</I>). You create an abstract class when you want to
manipulate a set of classes through this common interface. All derived-class
methods that match the signature of the base-class declaration will be called
using the dynamic binding mechanism. (However, as seen in the last section, if
the method’s name is the same as the base class but the arguments are
different, you’ve got overloading, which probably isn’t what you
want.)</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">If you have an abstract class like
<B>Instrument</B>, objects of that class almost always have no meaning. That is,
<B>Instrument</B> is meant to express only the interface, and not a particular
implementation, so creating an <B>Instrument</B> object makes no sense, and
you’ll probably want to prevent the user from doing it. This can be
accomplished by making all the methods in <B>Instrument</B> print error
messages, but this delays the information until run-time and requires reliable
exhaustive testing on the user’s part. It’s always better to catch
problems at compile time.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Java provides a mechanism for doing
this called the <I>abstract method</I>. This is a method that is incomplete; it
has only a declaration and no method body. Here is the syntax for an abstract
method declaration:</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><TT><FONT FACE="Courier New">abstract void
X();</FONT></TT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">A class containing abstract methods
is called an <I>abstract class</I>. If a class contains one or more abstract
methods, the class must be qualified as <B>abstract.</B> (Otherwise, the
compiler gives you an error message.)</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">If an abstract class is incomplete,
what is the compiler supposed to do when someone tries to make an object of that
class? It cannot safely create an object of an abstract class, so you get an
error message from the compiler. This way the compiler ensures the purity of the
abstract class, and you don’t need to worry about misusing
it.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">If you
<A NAME="Index584"></A><A NAME="Index585"></A><A NAME="Index586"></A>inherit
from an abstract class and you want to make objects of the new type, you must
provide method definitions for all the abstract methods in the base class. If
you don’t (and you may choose not to), then the derived class is also
abstract and the compiler will force you to qualify <I>that</I> class with the
<A NAME="Index587"></A><B>abstract</B> keyword.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">It’s possible to declare a
class as <B>abstract</B> <I>without</I> including any <B>abstract</B> methods.
This is useful when you’ve got a class in which it doesn’t make
sense to have any <B>abstract</B> methods, and yet you want to prevent any
instances of that class.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The <B>Instrument</B> class can
easily be turned into an abstract class. Only some of the methods will be
abstract, since making a class abstract doesn’t force you to make all the
methods abstract.<B> </B>Here’s what it looks like:</FONT><BR></P></DIV>
<DIV ALIGN="CENTER"><FONT FACE="Georgia"><IMG SRC="Tjava109.gif"></FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Here’s the orchestra example
modified to use <B>abstract</B> classes and methods:</FONT><BR></P></DIV>
<BLOCKQUOTE><FONT SIZE = "+1"><PRE><font color=#009900>//: Music4.java</font>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -