📄 chap07.htm
字号:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<HTML>
<!--
This document was converted from RTF source:
By rtftohtml 4.19
See http://www.sunpack.com/RTF
Filename:TIJ2.rtf
Application Directory:C:\TOOLS\RTF2HTML\
Subject:
Author:Bruce Eckel
Operator:Bruce Eckel
Document Comments:
Version Comments:
Comments:
Keywords:
Translation Date:05/21/2001
Translation Time:10:39:09
Translation Platform:Win32
Number of Output files:23
This File:Chap07.htm
SplitDepth=1
SkipNavPanel=1
SkipLeadingToc=1
SkipTrailingToc=1
GenContents=1
GenFrames=1
GenIndex=1
-->
<HEAD lang="en"><META http-equiv="Content-Type" content="text/html">
<TITLE>7: Polymorphism</TITLE>
</HEAD>
<BODY BGCOLOR="#FFFFFF"><DIV ALIGN="CENTER">
<a href="http://www.MindView.net">
<img src="mindview.gif" alt="MindView Inc." BORDER = "0"></a>
<CENTER>
<FONT FACE="Verdana" size = "-1">
[ <a href="README.txt">Viewing Hints</a> ]
[ <a href="RevHist.htm">Revision History</a> ]
[ <a href="http://www.mindview.net/Books/TIJ/">Book Home Page</a> ]
[ <a href="http://www.mindview.net/Etc/MailingList.html">Free Newsletter</a> ] <br>
[ <a href="http://www.mindview.net/Seminars">Seminars</a> ]
[ <a href="http://www.mindview.net/CDs">Seminars on CD ROM</a> ]
[ <a href="http://www.mindview.net/Services">Consulting</a> ]
</FONT>
<H2><FONT FACE="Verdana">
Thinking in Java, 2nd edition, Revision 12</FONT></H2>
<H3><FONT FACE="Verdana">©2000 by Bruce Eckel</FONT></H3>
<FONT FACE="Verdana" size = "-1">
[ <a href="Chap06.htm">Previous Chapter</a> ]
[ <a href="SimpCont.htm">Short TOC</a> ]
[ <a href="Contents.htm">Table of Contents</a> ]
[ <a href="DocIdx.htm">Index</a> ]
[ <a href="Chap08.htm">Next Chapter</a> ]
</FONT>
</CENTER>
</P></DIV><A NAME="Chapter_7"></A><A NAME="_Toc375545326"></A><A NAME="_Toc477690727"></A><A NAME="_Toc481064624"></A><A NAME="Heading234"></A><FONT FACE = "Verdana"><H1 ALIGN="LEFT">
7: Polymorphism<A NAME="A"></A></H1></FONT>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia" SIZE=4><backtalk:display ID=TIJ3_CHAPTER7_I0>
<A NAME="Index666"></A>Polymorphism
is the third essential feature of an object-oriented programming language, after
data abstraction and inheritance. </FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">It provides another dimension of
separation of interface from implementation, to decouple <I>what</I> from
<I>how</I>. Polymorphism allows improved code organization and readability as
well as the creation of <I>extensible</I> programs that can be
“grown” not only during the original creation of the project but
also when new features are desired.
</backtalk:display>
[ <a href='http://www.mindview.net/backtalk/CommentServlet?ID=TIJ3_CHAPTER7_I0'
target="_blank">Add Comment</a> ]
<backtalk:display ID=TIJ3_CHAPTER7_I1>
</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Encapsulation creates new data types by
combining characteristics and behaviors. Implementation hiding separates the
interface from the implementation by making the details <B>private</B>. This
sort of mechanical organization makes ready sense to someone with a procedural
programming background. But polymorphism deals with
<A NAME="Index667"></A>decoupling in terms of <I>types</I>. In the last chapter,
you saw how <A NAME="Index668"></A>inheritance allows the treatment of an object
as its own type <I>or</I> its base type. This ability is critical because it
allows many types (derived from the same base type) to be treated as if they
were one type, and a single piece of code to work on all those different types
equally. The <A NAME="Index669"></A>polymorphic method call allows one type to
express its distinction from another, similar type, as long as they’re
both derived from the same base type. This distinction is expressed through
differences in behavior of the methods that you can call through the base class.
</backtalk:display>
[ <a href='http://www.mindview.net/backtalk/CommentServlet?ID=TIJ3_CHAPTER7_I1'
target="_blank">Add Comment</a> ]
<backtalk:display ID=TIJ3_CHAPTER7_I2>
</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">In this chapter, you’ll learn about
polymorphism (also called
<A NAME="Index670"></A><A NAME="Index671"></A><A NAME="Index672"></A><A NAME="Index673"></A><I>dynamic
binding</I> or <I>late binding</I> or <I>run-time binding</I>)<I> </I>starting
from the basics, with simple examples that strip away everything but the
polymorphic behavior of the program.
</backtalk:display>
[ <a href='http://www.mindview.net/backtalk/CommentServlet?ID=TIJ3_CHAPTER7_I2'
target="_blank">Add Comment</a> ]
<backtalk:display ID=TIJ3_CHAPTER7_I3>
</FONT><A NAME="_Toc305593263"></A><A NAME="_Toc305628735"></A><A NAME="_Toc312374040"></A><A NAME="_Toc375545327"></A><A NAME="_Toc481064625"></A><BR></P></DIV>
<A NAME="Heading235"></A><FONT FACE = "Verdana"><H2 ALIGN="LEFT">
Upcasting revisited</H2></FONT>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">In Chapter 6 you saw how an object can be
used as its own type or as an object of its base type. Taking an object
reference and treating it as a reference to its base type is called
<I>upcasting,<A NAME="Index674"></A></I> because of the way inheritance trees
are drawn with the base class at the top.
</backtalk:display>
[ <a href='http://www.mindview.net/backtalk/CommentServlet?ID=TIJ3_CHAPTER7_I3'
target="_blank">Add Comment</a> ]
<backtalk:display ID=TIJ3_CHAPTER7_I4>
</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">You also saw a problem arise, which is
embodied in the following:</FONT><BR></P></DIV>
<BLOCKQUOTE><FONT SIZE = "+1"><PRE><font color=#009900>//: c07:music:Music.java </font>
<font color=#009900>// Inheritance & upcasting.</font>
<font color=#0000ff>class</font> Note {
<font color=#0000ff>private</font> <font color=#0000ff>int</font> value;
<font color=#0000ff>private</font> Note(<font color=#0000ff>int</font> val) { value = val; }
<font color=#0000ff>public</font> <font color=#0000ff>static</font> <font color=#0000ff>final</font> Note
MIDDLE_C = <font color=#0000ff>new</font> Note(0),
C_SHARP = <font color=#0000ff>new</font> Note(1),
B_FLAT = <font color=#0000ff>new</font> Note(2);
} <font color=#009900>// Etc.</font>
<font color=#0000ff>class</font> Instrument {
<font color=#0000ff>public</font> <font color=#0000ff>void</font> play(Note n) {
System.out.println(<font color=#004488>"Instrument.play()"</font>);
}
}
<font color=#009900>// Wind objects are instruments</font>
<font color=#009900>// because they have the same interface:</font>
<font color=#0000ff>class</font> Wind <font color=#0000ff>extends</font> Instrument {
<font color=#009900>// Redefine interface method:</font>
<font color=#0000ff>public</font> <font color=#0000ff>void</font> play(Note n) {
System.out.println(<font color=#004488>"Wind.play()"</font>);
}
}
<font color=#0000ff>public</font> <font color=#0000ff>class</font> Music {
<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> main(String[] args) {
Wind flute = <font color=#0000ff>new</font> Wind();
tune(flute); <font color=#009900>// Upcasting</font>
}
} <font color=#009900>///:~</font></PRE></FONT></BLOCKQUOTE>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The method <B>Music.tune( )</B>
accepts an <B>Instrument </B>reference, but also anything derived from
<B>Instrument</B>. In <B>main( )</B>, you can see this happening as a
<B>Wind</B> reference is passed to <B>tune( )</B>, with no cast necessary.
This is acceptable; the interface in <B>Instrument</B> must exist in
<B>Wind</B>, because <B>Wind</B> is inherited from <B>Instrument</B>. Upcasting
from <B>Wind</B> to <B>Instrument</B> may “narrow” that interface,
but it cannot make it anything less than the full interface to
<B>Instrument</B>.
</backtalk:display>
[ <a href='http://www.mindview.net/backtalk/CommentServlet?ID=TIJ3_CHAPTER7_I4'
target="_blank">Add Comment</a> ]
<backtalk:display ID=TIJ3_CHAPTER7_I5>
</FONT><A NAME="_Toc481064626"></A><BR></P></DIV>
<A NAME="Heading236"></A><FONT FACE = "Verdana"><H3 ALIGN="LEFT">
Forgetting the object type</H3></FONT>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">This program might seem strange to you.
Why should anyone intentionally <I>forget</I> the type of an object? This is
what happens when you upcast, and it seems like it could be much more
straightforward if <B>tune( )</B> simply takes a <B>Wind</B> reference as
its argument. This brings up an essential point: If you did that, you’d
need to write a new <B>tune( )</B> for every type of <B>Instrument</B> in
your system. Suppose we follow this reasoning and add <B>Stringed</B> and
<B>Brass</B> instruments:</FONT><BR></P></DIV>
<BLOCKQUOTE><FONT SIZE = "+1"><PRE><font color=#009900>//: c07:music2:Music2.java </font>
<font color=#009900>// Overloading instead of upcasting.</font>
<font color=#0000ff>class</font> Note {
<font color=#0000ff>private</font> <font color=#0000ff>int</font> value;
<font color=#0000ff>private</font> Note(<font color=#0000ff>int</font> val) { value = val; }
<font color=#0000ff>public</font> <font color=#0000ff>static</font> <font color=#0000ff>final</font> Note
MIDDLE_C = <font color=#0000ff>new</font> Note(0),
C_SHARP = <font color=#0000ff>new</font> Note(1),
B_FLAT = <font color=#0000ff>new</font> Note(2);
} <font color=#009900>// Etc.</font>
<font color=#0000ff>class</font> Instrument {
<font color=#0000ff>public</font> <font color=#0000ff>void</font> play(Note n) {
System.out.println(<font color=#004488>"Instrument.play()"</font>);
}
}
<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>);
}
}
<font color=#0000ff>class</font> Stringed <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>"Stringed.play()"</font>);
}
}
<font color=#0000ff>class</font> Brass <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>"Brass.play()"</font>);
}
}
<font color=#0000ff>public</font> <font color=#0000ff>class</font> Music2 {
<font color=#0000ff>public</font> <font color=#0000ff>static</font> <font color=#0000ff>void</font> tune(Wind i) {
i.play(Note.MIDDLE_C);
}
<font color=#0000ff>public</font> <font color=#0000ff>static</font> <font color=#0000ff>void</font> tune(Stringed i) {
i.play(Note.MIDDLE_C);
}
<font color=#0000ff>public</font> <font color=#0000ff>static</font> <font color=#0000ff>void</font> tune(Brass i) {
i.play(Note.MIDDLE_C);
}
<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();
Stringed violin = <font color=#0000ff>new</font> Stringed();
Brass frenchHorn = <font color=#0000ff>new</font> Brass();
tune(flute); <font color=#009900>// No upcasting</font>
tune(violin);
tune(frenchHorn);
}
} <font color=#009900>///:~</font></PRE></FONT></BLOCKQUOTE>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">This works, but there’s a major
drawback: You must write type-specific methods for each new <B>Instrument</B>
class you add. This means more programming in the first place, but it also means
that if you want to add a new method like <B>tune( )</B> or a new type of
<B>Instrument</B>, you’ve got a lot of work to do. Add the fact that the
compiler won’t give you any error messages if you forget to overload one
of your methods and the whole process of working with types becomes
unmanageable.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -