📄 tij308.htm
字号:
} <font color=#009900>///:~</font></PRE></FONT></BLOCKQUOTE><p><br></p>
<p>You can see that all the overloaded methods of <b>Homer </b>are available in <b>Bart</b>, even though <b>Bart </b>introduces a new overloaded method (in C++ doing this would hide the base-class methods). As you’ll see in the next chapter, it’s far more common to override methods of the same name, using exactly the same signature and return type as in the base class. It can be confusing otherwise (which is why C++ disallows it—to prevent you from making what is probably a mistake). <font size="-2"><a href="mailto:TIJ3@MindView.net?Subject=[TIJ3]Chap06_947" title="Send BackTalk Comment">Feedback</a></font><br></p>
<h2>
<a name="_Toc305593255"></a><a name="_Toc305628727"></a><a name="_Toc312374023"></a><a name="_Toc375545312"></a><a name="_Toc24775641"></a><a name="Heading5548"></a>Choosing
composition <br>vs. inheritance<br></h2>
<p><a name="Index539"></a><a name="Index540"></a>Both composition and inheritance allow you to place subobjects inside your new class (composition explicitly does this—with inheritance it’s implicit). You might wonder about the difference between the two, and when to choose one over the other. <font size="-2"><a href="mailto:TIJ3@MindView.net?Subject=[TIJ3]Chap06_948" title="Send BackTalk Comment">Feedback</a></font><br></p>
<p><a name="Index541"></a>Composition is generally used when you want the features of an existing class inside your new class, but not its interface. That is, you embed an object so that you can use it to implement functionality in your new class, but the user of your new class sees the interface you’ve defined for the new class rather than the interface from the embedded object. For this effect, you embed <b>private</b> objects of existing classes inside your new class.<a name="Index542"></a><a name="Index543"></a> <font size="-2"><a href="mailto:TIJ3@MindView.net?Subject=[TIJ3]Chap06_949" title="Send BackTalk Comment">Feedback</a></font><br></p>
<p>Sometimes it makes sense to allow the class user to directly access the composition of your new class; that is, to make the member objects <b>public</b>. The member objects use implementation hiding themselves, so this is a safe thing to do. When the user knows you’re assembling a bunch of parts, it makes the interface easier to understand. A <b>car</b> object is a good example: <font size="-2"><a href="mailto:TIJ3@MindView.net?Subject=[TIJ3]A0110" title="Send BackTalk Comment">Feedback</a></font><br></p>
<BLOCKQUOTE><FONT SIZE = "+1"><PRE><font color=#009900>//: c06:Car.java</font>
<font color=#009900>// Composition with public objects.</font>
<font color=#0000ff>class</font> Engine {
<font color=#0000ff>public</font> <font color=#0000ff>void</font> start() {}
<font color=#0000ff>public</font> <font color=#0000ff>void</font> rev() {}
<font color=#0000ff>public</font> <font color=#0000ff>void</font> stop() {}
}
<font color=#0000ff>class</font> Wheel {
<font color=#0000ff>public</font> <font color=#0000ff>void</font> inflate(<font color=#0000ff>int</font> psi) {}
}
<font color=#0000ff>class</font> Window {
<font color=#0000ff>public</font> <font color=#0000ff>void</font> rollup() {}
<font color=#0000ff>public</font> <font color=#0000ff>void</font> rolldown() {}
}
<font color=#0000ff>class</font> Door {
<font color=#0000ff>public</font> Window window = <font color=#0000ff>new</font> Window();
<font color=#0000ff>public</font> <font color=#0000ff>void</font> open() {}
<font color=#0000ff>public</font> <font color=#0000ff>void</font> close() {}
}
<font color=#0000ff>public</font> <font color=#0000ff>class</font> Car {
<font color=#0000ff>public</font> Engine engine = <font color=#0000ff>new</font> Engine();
<font color=#0000ff>public</font> Wheel[] wheel = <font color=#0000ff>new</font> Wheel[4];
<font color=#0000ff>public</font> Door
left = <font color=#0000ff>new</font> Door(),
right = <font color=#0000ff>new</font> Door(); <font color=#009900>// 2-door</font>
<font color=#0000ff>public</font> Car() {
<font color=#0000ff>for</font>(<font color=#0000ff>int</font> i = 0; i < 4; i++)
wheel[i] = <font color=#0000ff>new</font> Wheel();
}
<font color=#0000ff>public</font> <font color=#0000ff>static</font> <font color=#0000ff>void</font> main(String[] args) {
Car car = <font color=#0000ff>new</font> Car();
car.left.window.rollup();
car.wheel[0].inflate(72);
}
} <font color=#009900>///:~</font></PRE></FONT></BLOCKQUOTE><p><br></p>
<p>Because in this case the composition of a car is part of the analysis of the problem (and not simply part of the underlying design), making the members <b>public</b> assists the client programmer’s understanding of how to use the class and requires less code complexity for the creator of the class. However, keep in mind that this is a special case, and that in general you should make fields <b>private</b>. <font size="-2"><a href="mailto:TIJ3@MindView.net?Subject=[TIJ3]Chap06_950" title="Send BackTalk Comment">Feedback</a></font><br></p>
<p><a name="Index544"></a><a name="Index545"></a>When you inherit, you take an existing class and make a special version of it. In general, this means that you’re taking a general-purpose class and specializing it for a particular need. With a little thought, you’ll see that it would make no sense to compose a car using a vehicle object—a car doesn’t contain a vehicle, it <i>is</i> a vehicle. The <i>is-a</i><a name="Index546"></a> relationship is expressed with inheritance, and the <a name="Index547"></a><i>has-a</i> relationship is expressed with composition. <font size="-2"><a href="mailto:TIJ3@MindView.net?Subject=[TIJ3]Chap06_951" title="Send BackTalk Comment">Feedback</a></font><br></p>
<h2>
<a name="_Toc305593256"></a><a name="_Toc305628728"></a><a name="_Toc312374027"></a><a name="_Toc375545313"></a><a name="_Toc24775642"></a><a name="Heading5595"></a>protected</h2>
<p>Now that you’ve been introduced to inheritance, the keyword <b>protected</b> finally has meaning. In an ideal world, the <a name="Index548"></a><b>private</b> keyword would be enough. In real projects, there are times when you want to make something hidden from the world at large and yet allow access for members of derived classes. The <b>protected</b> keyword is a nod to pragmatism. It says “This is <b>private</b> as far as the class user is concerned, but available to anyone who inherits from this class or anyone else in the same <b>package</b>.” (In Java, <a name="Index549"></a><a name="Index550"></a><b>protected</b> also provides package access.) <font size="-2"><a href="mailto:TIJ3@MindView.net?Subject=[TIJ3]Chap06_952" title="Send BackTalk Comment">Feedback</a></font><br></p>
<p>The best approach is to leave the fields <a name="Index551"></a><b>private</b>; you should always preserve your right to change the underlying implementation. You can then allow controlled access to inheritors of your class through <a name="Index552"></a><b>protected </b>methods:<br></p>
<BLOCKQUOTE><FONT SIZE = "+1"><PRE><font color=#009900>//: c06:Orc.java</font>
<font color=#009900>// The protected keyword.</font>
<font color=#0000ff>import</font> com.bruceeckel.simpletest.*;
<font color=#0000ff>import</font> java.util.*;
<font color=#0000ff>class</font> Villain {
<font color=#0000ff>private</font> String name;
<font color=#0000ff>protected</font> <font color=#0000ff>void</font> set(String nm) { name = nm; }
<font color=#0000ff>public</font> Villain(String name) { <font color=#0000ff>this</font>.name = name; }
<font color=#0000ff>public</font> String toString() {
<font color=#0000ff>return</font> <font color=#004488>"I'm a Villain and my name is "</font> + name;
}
}
<font color=#0000ff>public</font> <font color=#0000ff>class</font> Orc <font color=#0000ff>extends</font> Villain {
<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>int</font> orcNumber;
<font color=#0000ff>public</font> Orc(String name, <font color=#0000ff>int</font> orcNumber) {
<font color=#0000ff>super</font>(name);
<font color=#0000ff>this</font>.orcNumber = orcNumber;
}
<font color=#0000ff>public</font> <font color=#0000ff>void</font> change(String name, <font color=#0000ff>int</font> orcNumber) {
set(name); <font color=#009900>// Available because it's protected</font>
<font color=#0000ff>this</font>.orcNumber = orcNumber;
}
<font color=#0000ff>public</font> String toString() {
<font color=#0000ff>return</font> <font color=#004488>"Orc "</font> + orcNumber + <font color=#004488>": "</font> + <font color=#0000ff>super</font>.toString();
}
<font color=#0000ff>public</font> <font color=#0000ff>static</font> <font color=#0000ff>void</font> main(String[] args) {
Orc orc = <font color=#0000ff>new</font> Orc(<font color=#004488>"Limburger"</font>, 12);
System.out.println(orc);
orc.change(<font color=#004488>"Bob"</font>, 19);
System.out.println(orc);
monitor.expect(<font color=#0000ff>new</font> String[] {
<font color=#004488>"Orc 12: I'm a Villain and my name is Limburger"</font>,
<font color=#004488>"Orc 19: I'm a Villain and my name is Bob"</font>
});
}
} <font color=#009900>///:~</font></PRE></FONT></BLOCKQUOTE><p><br></p>
<p>You can see that <b>change( )</b> has access to <b>set( )</b> because it’s <b>protected</b>. Also note the way that <b>Orc</b>’s <b>toString( )</b> method is defined in terms of the base-class version of <b>toString( )</b>.<b> </b><font size="-2"><a href="mailto:TIJ3@MindView.net?Subject=[TIJ3]Chap06_953" title="Send BackTalk Comment">Feedback</a></font><br></p>
<h2>
<a name="_Toc305593258"></a><a name="_Toc305628730"></a><a name="_Toc312374030"></a><a name="_Toc375545314"></a><a name="_Toc24775643"></a><a name="Heading5639"></a>Incremental
development</h2>
<p>One of the advantages of inheritance is that it supports <i>incremental development</i>. You can introduce new code without causing bugs in existing code; in fact, you isolate new bugs inside the new code. By inheriting from an existing, functional class and adding fields and methods (and redefining existing methods), you leave the existing code—that someone else might still be using—untouched and unbugged. If a bug happens, you know that it’s in your new code, which is much shorter and easier to read than if you had modified the body of existing code. <font size="-2"><a href="mailto:TIJ3@MindView.net?Subject=[TIJ3]Chap06_954" title="Send BackTalk Comment">Feedback</a></font><br></p>
<p><a name="Index553"></a><a name="Index554"></a>It’s rather amazing how cleanly the classes are separated. You don’t even need the source code for the methods in order to reuse the code. At most, you just import a package. (This is true for both inheritance and composition.) <font size="-2"><a href="mailto:TIJ3@MindView.net?Subject=[TIJ3]Chap06_955" title="Send BackTalk Comment">Feedback</a></font><br></p>
<p>It’s important to realize that program development is an incremental process, just like human learning. You can do as much analysis as you want, but you still won’t know all the answers when you set out on a project. You’ll have much more success—and more immediate feedback—if you start out to “grow” your project as an organic, evolutionary creature, rather than constructing it all at once like a glass-box skyscraper. <font size="-2"><a href="mailto:TIJ3@MindView.net?Subject=[TIJ3]Chap06_956" title="Send BackTalk Comment">Feedback</a></font><br></p>
<p>Although inheritance for experimentation can be a useful technique, at some point after things stabilize you need to take a new look at your class hierarchy with an eye to collapsing it into a sensible structure. Remember that underneath it all, inheritance is meant to express a relationship that says: “This new class is a <i>type of</i> that old class.” Your program should not be concerned with pushing bits around, but instead with creating and manipulating objects of various types to express a model in the terms that come from the problem space. <font size="-2"><a href="mailto:TIJ3@MindView.net?Subject=[TIJ3]Chap06_957" title="Send BackTalk Comment">Feedback</a></font><br></p>
<h2>
<a name="Index555"></a><a name="_Toc305593259"></a><a name="_Toc305628731"></a><a name="_Toc312374031"></a><a name="_Toc375545315"></a><a name="_Toc24775644"></a><a name="Heading5644"></a>Upcasting<br></h2>
<p><a name="Index556"></a>The most important aspect of inheritance is not that it provides methods for the new class. It’s the relationship expressed between the new class and the base class. This relationship can be summarized by saying, “The new class <a name="Index557"></a><i>is a type of</i> the existing class.” <font size="-2"><a href="mailto:TIJ3@MindView.net?Subject=[TIJ3]Chap06_958" title="Send BackTalk Comment">Feedback</a></font><br></p>
<p>This description is not just a fanciful way of explaining inheritance—it’s supported directly by the language. As an example, consider a base class called <b>Instrument</b> that represents musical instruments, and a derived class called <b>Wind</b>. Because inheritance means that all of the methods in the base class are also available in the derived class, any message you can send to the base class can also be sent to the derived class. If the <b>Instrument</b> class has a <b>play( )</b> method, so will <b>Wind</b> instruments. This means we can accurately say that a <b>Wind</b> object is also a type of <b>Instrument</b>. The following example shows how the compiler supports this notion: <font size="-2"><a href="mailto:TIJ3@MindView.net?Subject=[TIJ3]A0111" title="Send BackTalk Comment">Feedback</a></font><br></p>
<BLOCKQUOTE><FONT SIZE = "+1"><PRE><font color=#009900>//: c06:Wind.java</font>
<font color=#009900>// Inheritance & upcasting.</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() {}
<font color=#0000ff>static</font> <font color=#0000ff>void</font> tune(Instrument i) {
<font color=#009900>// ...</font>
i.play();
}
}
<font color=#009900>// Wind objects are instruments</font>
<font color=#009900>// because they have the same interface:</font>
<font color=#0000ff>public</font> <font color=#0000ff>class</font> Wind <font color=#0000ff>extends</font> Instrument {
<font color=#0000ff>public</font> <font color=#0000ff>static</font> <font color=#0000ff>void</font> main(String[] args) {
Wind flute = <font color=#0000ff>new</font> Wind();
Instrument.tune(flute); <font color=#009900>// Upcasting</font>
}
} <font color=#009900>///:~</font></PRE></FONT></BLOCKQUOTE><p><br></p>
<p>What’s interesting in this example is the <b>tune( )</b> method, which accepts an <b>Instrument</b> reference. However, in <b>Wind</b>.<b>main( )</b> the <b>tune( )</b> method is called by giving it a <b>Wind</b> reference. Given that Java is particular about type checking, it seems strange that a method that accepts one type will readily accept another type, until you realize that a <b>Wind</b> object is also an <b>Instrument</b> object, and there’s no method that <b>tune( )</b> could call for an <b>Instrument</b> that isn’t also in <b>Wind</b>. Inside <b>tune( )</b>, the code works for <b>Instrument</b> and anything derived from <b>Instrument</b>, and the act of converting a <b>Wind</b> reference into an <b>Instrument</b> reference is called <i>upcasting</i>. <font size="-2"><a href="mailto:TIJ3@MindView.net?Subject=[TIJ3]Chap06_959" title="Send BackTalk Comment">Feedback</a></font><br></p>
<h3>
<a name="_Toc312374032"></a><a name="_Toc375545316"></a><a name="_Toc24775645"></a><a name="Heading5669"></a>Why
“upcasting”?</h3>
<p>The reason for the term is historical, and based on the way class inheritance diagrams have traditionally been drawn: with the root at the top of the page, growing downward. (Of course, you can draw your diagrams any way you find helpful.) The inheritance diagram for <a name="Index558"></a><a name="Index559"></a><a name="Index560"></a><b>Wind.java</b> is then: <font size="-2"><a href="mailto:TIJ3@MindView.net?Subject=[TIJ3]A0112" title="Send BackTalk Comment">Feedback</a></font><br></p>
<p align="center"><img src="TIJ313.png" alt="TIJ313.png" border="0" ><br></p>
<p>Casting from a derived type to a base type moves <i>up</i> on the inheritance diagram, so it’s commonly referred to as <i>upcasting</i>. Upcasting is always safe because you’re going from a more specific type to a more general type. That is, the derived class is a superset of the base class. It might contain more methods than the base class, but it must contain <i>at least</i> the methods in the base class. The only thing that can occur to the class interface during the upcast is that it can lose methods, not gain them. This is why the compiler allows upcasting without any explicit casts or other spe
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -