📄 tij0023.html
字号:
<html><body>
<table width="100%"><tr>
<td>
<a href="http://www.bruceeckel.com/javabook.html">Bruce Eckel's Thinking in Java</a>
</td>
<td align="right">
<a href="tij_c.html">Contents</a> | <a href="tij0022.html">Prev</a> | <a href="tij0024.html">Next</a>
</td>
</tr></table>
<hr>
<H2 ALIGN=LEFT>
Interchangeable
objects
<P>with
polymorphism
</H2>
<DIV ALIGN=LEFT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">Inheritance
usually ends up creating a family of classes, all based on the same uniform
interface. We express this with an inverted tree diagram:
</FONT><A NAME="fnB5" HREF="#fn5">[5]</A><P></DIV><DIV ALIGN=LEFT><P></DIV><DIV ALIGN=LEFT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">One
of the most important things you do with such a family of classes is to treat
an object of a derived class as an object of the base class. This is important
because it means you can write a single piece of code that ignores the specific
details of type and talks just to the base class. That code is then
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><I>decoupled</I></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
from type-specific information, and thus is simpler to write and easier to
understand. And, if a new type – a
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>Triangle</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">,
for example –
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>
</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">is
added through inheritance, the code you write will work just as well for the
new type of
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>Shape</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
as it did on the existing types. Thus the program is
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><I>extensible</I></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">Consider
the above example. If you write a function in Java:
</FONT><P></DIV>
<font color="#990000"><PRE><font color="#0000ff">void</font> doStuff(Shape s) {
s.erase();
<font color="#009900">// ...</font>
s.draw();
}</PRE></font><DIV ALIGN=LEFT><P></DIV><DIV ALIGN=LEFT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">This
function speaks to any
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>Shape</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">,
so it is independent of the specific type of object it’s drawing and
erasing. If in some other program we use the
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>doStuff( )</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
function:
</FONT><P></DIV>
<font color="#990000"><PRE>Circle c = <font color="#0000ff">new</font> Circle();
Triangle t = <font color="#0000ff">new</font> Triangle();
Line l = <font color="#0000ff">new</font> Line();
doStuff(c);
doStuff(t);
doStuff(l);</PRE></font><DIV ALIGN=LEFT><P></DIV><DIV ALIGN=LEFT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">The
calls to
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>doStuff( )
</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">automatically
work right, regardless of the exact type of the object.
</FONT><P></DIV><DIV ALIGN=LEFT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">This
is actually a pretty amazing trick. Consider the line:
</FONT><P></DIV><DIV ALIGN=LEFT><TT><FONT FACE="Courier New" SIZE=3 COLOR="Black">doStuff(c);</FONT></TT><P></DIV><DIV ALIGN=LEFT><P></DIV><DIV ALIGN=LEFT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">What’s
happening here is that a
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>Circle</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
handle is being passed into a function that’s expecting a
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>Shape</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
handle. Since 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"><I>is</I></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
a
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>Shape</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
it can be treated as one by
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>doStuff( )</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">.
That is, any message that
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>doStuff( )</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
can send to a
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>Shape</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">,
a
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>Circle</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
can accept. So it is a completely safe and logical thing to do.
</FONT><P></DIV><DIV ALIGN=LEFT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">We
call this process of treating a derived type as though it were its base type
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><I>upcasting</I></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">.
The name
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><I>cast
</I></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">is
used in the sense of casting into a mold and the
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><I>up</I></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
comes from the way the inheritance diagram is typically arranged, with the base
type at the top and the derived classes fanning out downward. Thus, casting to
a base type is moving up the inheritance diagram: upcasting.
</FONT><P></DIV><DIV ALIGN=LEFT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">An
object-oriented program contains some upcasting somewhere, because that’s
how you decouple yourself from knowing about the exact type you’re
working with. Look at the code in
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>doStuff( )</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">:</FONT><P></DIV>
<font color="#990000"><PRE> s.erase();
<font color="#009900">// ...</font>
s.draw(); </PRE></font><DIV ALIGN=LEFT><P></DIV><DIV ALIGN=LEFT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">Notice
that it doesn’t say “If you’re a
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>Circle</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">,
do this, if you’re a
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>Square</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">,
do that, etc.” If you write that kind of code, which checks for all the
possible types a
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>Shape</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
can actually be, it’s messy and you need to change it every time you add
a new kind of
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>Shape</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">.
Here, you just say “You’re a shape, I know you can
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>erase( )
</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">yourself,
do it and take care of the details correctly.”
</FONT><a name="_Toc375545196"></a><a name="_Toc408018393"></a><P></DIV>
<A NAME="Heading27"></A><H3 ALIGN=LEFT>
Dynamic
binding
</H3>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -