📄 tij309.htm
字号:
<font color=#0000ff>class</font> Wind <font color=#0000ff>extends</font> Instrument {
<font color=#0000ff>void</font> play(Note n) {
System.out.println(<font color=#004488>"Wind.play() "</font> + n);
}
String what() { <font color=#0000ff>return</font> <font color=#004488>"Wind"</font>; }
<font color=#0000ff>void</font> adjust() {}
}
<font color=#0000ff>class</font> Percussion <font color=#0000ff>extends</font> Instrument {
<font color=#0000ff>void</font> play(Note n) {
System.out.println(<font color=#004488>"Percussion.play() "</font> + n);
}
String what() { <font color=#0000ff>return</font> <font color=#004488>"Percussion"</font>; }
<font color=#0000ff>void</font> adjust() {}
}
<font color=#0000ff>class</font> Stringed <font color=#0000ff>extends</font> Instrument {
<font color=#0000ff>void</font> play(Note n) {
System.out.println(<font color=#004488>"Stringed.play() "</font> + n);
}
String what() { <font color=#0000ff>return</font> <font color=#004488>"Stringed"</font>; }
<font color=#0000ff>void</font> adjust() {}
}
<font color=#0000ff>class</font> Brass <font color=#0000ff>extends</font> Wind {
<font color=#0000ff>void</font> play(Note n) {
System.out.println(<font color=#004488>"Brass.play() "</font> + n);
}
<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>void</font> play(Note n) {
System.out.println(<font color=#004488>"Woodwind.play() "</font> + n);
}
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=#0000ff>private</font> <font color=#0000ff>static</font> Test monitor = <font color=#0000ff>new</font> Test();
<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>public</font> <font color=#0000ff>static</font> <font color=#0000ff>void</font> tune(Instrument i) {
<font color=#009900>// ...</font>
i.play(Note.MIDDLE_C);
}
<font color=#0000ff>public</font> <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 < e.length; i++)
tune(e[i]);
}
<font color=#0000ff>public</font> <font color=#0000ff>static</font> <font color=#0000ff>void</font> main(String[] args) {
<font color=#009900>// Upcasting during addition to the array:</font>
Instrument[] orchestra = {
<font color=#0000ff>new</font> Wind(),
<font color=#0000ff>new</font> Percussion(),
<font color=#0000ff>new</font> Stringed(),
<font color=#0000ff>new</font> Brass(),
<font color=#0000ff>new</font> Woodwind()
};
tuneAll(orchestra);
monitor.expect(<font color=#0000ff>new</font> String[] {
<font color=#004488>"Wind.play() Middle C"</font>,
<font color=#004488>"Percussion.play() Middle C"</font>,
<font color=#004488>"Stringed.play() Middle C"</font>,
<font color=#004488>"Brass.play() Middle C"</font>,
<font color=#004488>"Woodwind.play() Middle C"</font>
});
}
} <font color=#009900>///:~</font></PRE></FONT></BLOCKQUOTE><p><br></p>
<p>The new methods are <b>what( )</b>, which returns a <b>String</b> reference with a description of the class, and <b>adjust( )</b>, which provides some way to adjust each instrument. <font size="-2"><a href="mailto:TIJ3@MindView.net?Subject=[TIJ3]Chap07_1041" title="Send BackTalk Comment">Feedback</a></font><br></p>
<p>In <b>main( )</b>, when you place something inside the <b>orchestra </b>array, you automatically upcast to <b>Instrument</b>. <font size="-2"><a href="mailto:TIJ3@MindView.net?Subject=[TIJ3]Chap07_1042" title="Send BackTalk Comment">Feedback</a></font><br></p>
<p>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. 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.” <font size="-2"><a href="mailto:TIJ3@MindView.net?Subject=[TIJ3]Chap07_1043" title="Send BackTalk Comment">Feedback</a></font><br></p>
<h3>
<a name="_Toc375545334"></a><a name="_Toc24775662"></a><a name="Heading6306"></a>Pitfall:
“overriding” private methods</h3>
<p>Here’s something you might innocently try to do:<br></p>
<BLOCKQUOTE><FONT SIZE = "+1"><PRE><font color=#009900>//: c07:PrivateOverride.java</font>
<font color=#009900>// Abstract classes and methods.</font>
<font color=#0000ff>import</font> com.bruceeckel.simpletest.*;
<font color=#0000ff>public</font> <font color=#0000ff>class</font> PrivateOverride {
<font color=#0000ff>private</font> <font color=#0000ff>static</font> Test monitor = <font color=#0000ff>new</font> Test();
<font color=#0000ff>private</font> <font color=#0000ff>void</font> f() {
System.out.println(<font color=#004488>"private f()"</font>);
}
<font color=#0000ff>public</font> <font color=#0000ff>static</font> <font color=#0000ff>void</font> main(String[] args) {
PrivateOverride po = <font color=#0000ff>new</font> Derived();
po.f();
monitor.expect(<font color=#0000ff>new</font> String[] {
<font color=#004488>"private f()"</font>
});
}
}
<font color=#0000ff>class</font> Derived <font color=#0000ff>extends</font> PrivateOverride {
<font color=#0000ff>public</font> <font color=#0000ff>void</font> f() {
System.out.println(<font color=#004488>"public f()"</font>);
}
} <font color=#009900>///:~</font></PRE></FONT></BLOCKQUOTE><p><br></p>
<p>You might reasonably expect the output to be “<b>public f( )</b>”, but a <b>private</b> method is automatically final, and is also hidden from the derived class. So <b>Derived</b>’s <b>f( )</b> in this case is a brand new method; it’s not even overloaded, since the base-class version of <b>f( )</b> isn’t visible in <b>Derived</b>. <font size="-2"><a href="mailto:TIJ3@MindView.net?Subject=[TIJ3]A0466" title="Send BackTalk Comment">Feedback</a></font><br></p>
<p>The result of this is that only non-<b>private</b> methods may be overridden, but you should watch out for the appearance of overriding <b>private</b> 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 <b>private</b> base-class method in your derived class. <font size="-2"><a href="mailto:TIJ3@MindView.net?Subject=[TIJ3]A0467" title="Send BackTalk Comment">Feedback</a></font><br></p>
<h2>
<a name="_Toc24775663"></a><a name="Heading6334"></a>Abstract classes <br>and
methods</h2>
<p>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 size="-2"><a href="mailto:TIJ3@MindView.net?Subject=[TIJ3]Chap07_1047" title="Send BackTalk Comment">Feedback</a></font><br></p>
<p>The only reason to establish this common interface 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 <a name="Index640"></a><a name="Index641"></a><b>Instrument</b> an <i>abstract base class</i> (or simply an <a name="Index642"></a><a name="Index643"></a><a name="Index644"></a><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 size="-2"><a href="mailto:TIJ3@MindView.net?Subject=[TIJ3]Chap07_1048" title="Send BackTalk Comment">Feedback</a></font><br></p>
<p>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 that delays the information until run time and requires reliable exhaustive testing on the user’s part. It’s better to catch problems at compile time. <font size="-2"><a href="mailto:TIJ3@MindView.net?Subject=[TIJ3]Chap07_1049" title="Send BackTalk Comment">Feedback</a></font><br></p>
<p>Java provides a mechanism for doing this called the <i>abstract method</i>.<sup><a name="fnB32" href="#fn32">[32]</a></sup> 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:<br></p>
<BLOCKQUOTE><FONT SIZE = "+1"><PRE><font color=#0000ff>abstract</font> <font color=#0000ff>void</font> f();</PRE></FONT></BLOCKQUOTE><p><br></p>
<p>A class containing abstract methods is called an <i>abstract class</i>. If a class contains one or more abstract methods, the class itself must be qualified as <b>abstract</b>. (Otherwise, the compiler gives you an error message.) <font size="-2"><a href="mailto:TIJ3@MindView.net?Subject=[TIJ3]Chap07_1050" title="Send BackTalk Comment">Feedback</a></font><br></p>
<p>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 size="-2"><a href="mailto:TIJ3@MindView.net?Subject=[TIJ3]Chap07_1051" title="Send BackTalk Comment">Feedback</a></font><br></p>
<p>If you 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 <a name="Index645"></a><a name="Index646"></a><a name="Index647"></a><i>that</i> class with the <a name="Index648"></a><b>abstract</b> keyword. <font size="-2"><a href="mailto:TIJ3@MindView.net?Subject=[TIJ3]Chap07_1052" title="Send BackTalk Comment">Feedback</a></font><br></p>
<p>It’s possible to create a class as <b>abstract</b> without 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 size="-2"><a href="mailto:TIJ3@MindView.net?Subject=[TIJ3]Chap07_1053" title="Send BackTalk Comment">Feedback</a></font><br></p>
<p>The <b>Instrument</b> class can easily be turned into an <b>abstract</b> class. Only some of the methods will be <b>abstract</b>, since making a class abstract doesn’t force you to make all the methods <b>abstract</b>.<b> </b>Here’s what it looks like:<br></p>
<p align="center"><img src="TIJ316.png" alt="TIJ316.png" border="0" ><br></p>
<p>Here’s the orchestra example modified to use <b>abstract</b> classes and methods:<br></p>
<BLOCKQUOTE><FONT SIZE = "+1"><PRE><font color=#009900>//: c07:music4:Music4.java</font>
<font color=#009900>// Abstract classes and methods.</font>
<font color=#0000ff>package</font> c07.music4;
<font color=#0000ff>import</font> com.bruceeckel.simpletest.*;
<font color=#0000ff>import</font> java.util.*;
<font color=#0000ff>import</font> c07.music.Note;
<font color=#0000ff>abstract</font> <font color=#0000ff>class</font> Instrument {
<font color=#0000ff>private</font> <font color=#0000ff>int</font> i; <font color=#009900>// Storage allocated for each</font>
<font color=#0000ff>public</font> <font color=#0000ff>abstract</font> <font color=#0000ff>void</font> play(Note n);
<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>abstract</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(Note n) {
System.out.println(<font color=#004488>"Wind.play() "</font> + n);
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -