📄 chap07.htm
字号:
</backtalk:display>
[ <a href='http://www.mindview.net/backtalk/CommentServlet?ID=TIJ3_CHAPTER7_I5'
target="_blank">Add Comment</a> ]
<backtalk:display ID=TIJ3_CHAPTER7_I6>
</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Wouldn’t it be much nicer if you
could just write a single method that takes the
<A NAME="Index675"></A><A NAME="Index676"></A>base class as its argument, and
not any of the specific derived classes? That is, wouldn’t it be nice if
you could forget that there are
<A NAME="Index677"></A><A NAME="Index678"></A>derived classes, and write your
code to talk only to the base class?
</backtalk:display>
[ <a href='http://www.mindview.net/backtalk/CommentServlet?ID=TIJ3_CHAPTER7_I6'
target="_blank">Add Comment</a> ]
<backtalk:display ID=TIJ3_CHAPTER7_I7>
</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">That’s exactly what polymorphism
allows you to do. However, most programmers who come from a procedural
programming background have a bit of trouble with the way polymorphism works.
</backtalk:display>
[ <a href='http://www.mindview.net/backtalk/CommentServlet?ID=TIJ3_CHAPTER7_I7'
target="_blank">Add Comment</a> ]
<backtalk:display ID=TIJ3_CHAPTER7_I8>
</FONT><A NAME="_Toc305593264"></A><A NAME="_Toc305628736"></A><A NAME="_Toc312374041"></A><A NAME="_Toc375545329"></A><A NAME="_Toc481064627"></A><BR></P></DIV>
<A NAME="Heading237"></A><FONT FACE = "Verdana"><H2 ALIGN="LEFT">
The twist</H2></FONT>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The difficulty with
<B>Music</B>.<B>java</B> can be seen by running the program. The output is
<B>Wind.play( )</B>. This is clearly the desired output, but it
doesn’t seem to make sense that it would work that way. Look at the
<B>tune( )</B> method:</FONT><BR></P></DIV>
<BLOCKQUOTE><FONT SIZE = "+1"><PRE> <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);
}</PRE></FONT></BLOCKQUOTE>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">It receives an <B>Instrument</B>
reference. So how can the compiler possibly know that this <B>Instrument</B>
reference points to a <B>Wind</B> in this case and not a <B>Brass </B>or
<B>Stringed</B>? The compiler can’t. To get a deeper understanding of the
issue, it’s helpful to examine the subject of <I>binding</I>.
</backtalk:display>
[ <a href='http://www.mindview.net/backtalk/CommentServlet?ID=TIJ3_CHAPTER7_I8'
target="_blank">Add Comment</a> ]
<backtalk:display ID=TIJ3_CHAPTER7_I9>
</FONT><A NAME="_Toc312374042"></A><A NAME="_Toc375545330"></A><A NAME="_Toc481064628"></A><BR></P></DIV>
<A NAME="Heading238"></A><FONT FACE = "Verdana"><H3 ALIGN="LEFT">
Method-call binding<BR><A NAME="Index679"></A><A NAME="Index680"></A></H3></FONT>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Connecting a method call to a method body
is called <I>binding</I>. When binding is performed before the program is run
(by the compiler and linker, if there is one), it’s called <I>early
binding<A NAME="Index681"></A></I>. You might not have heard the term before
because it has never been an option with procedural languages. C compilers have
only one kind of method call, and that’s early binding.
</backtalk:display>
[ <a href='http://www.mindview.net/backtalk/CommentServlet?ID=TIJ3_CHAPTER7_I9'
target="_blank">Add Comment</a> ]
<backtalk:display ID=TIJ3_CHAPTER7_I10>
</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The confusing part of the above program
revolves around early binding because the compiler cannot know the correct
method to call when it has only an <B>Instrument</B> reference.
</backtalk:display>
[ <a href='http://www.mindview.net/backtalk/CommentServlet?ID=TIJ3_CHAPTER7_I10'
target="_blank">Add Comment</a> ]
<backtalk:display ID=TIJ3_CHAPTER7_I11>
</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The solution is called <I>late
binding<A NAME="Index682"></A><A NAME="Index683"></A></I>, which means that the
binding occurs at run-time based on the type of object. Late binding is also
called <I>dynamic binding<A NAME="Index684"></A><A NAME="Index685"></A></I> or
<I>run-time binding<A NAME="Index686"></A><A NAME="Index687"></A></I>. When a
language implements late binding, there must be some mechanism to determine the
type of the object at run-time and to call the appropriate method. That is, the
compiler still doesn’t know the object type, but the method-call mechanism
finds out and calls the correct method body. The late-binding mechanism varies
from language to language, but you can imagine that some sort of type
information must be installed in the objects.
</backtalk:display>
[ <a href='http://www.mindview.net/backtalk/CommentServlet?ID=TIJ3_CHAPTER7_I11'
target="_blank">Add Comment</a> ]
<backtalk:display ID=TIJ3_CHAPTER7_I12>
</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">All method binding in Java uses late
binding unless a method has been declared
<A NAME="Index688"></A><A NAME="Index689"></A><B>final</B>. This means that
ordinarily you don’t need to make any decisions about whether late binding
will occur—it happens automatically.
</backtalk:display>
[ <a href='http://www.mindview.net/backtalk/CommentServlet?ID=TIJ3_CHAPTER7_I12'
target="_blank">Add Comment</a> ]
<backtalk:display ID=TIJ3_CHAPTER7_I13>
</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Why would you declare a method
<B>final</B>? As noted in the last chapter, it prevents anyone from overriding
that method. Perhaps more important, it effectively “turns off”
dynamic binding, or rather it tells the compiler that dynamic binding
isn’t necessary. This allows the compiler to generate slightly more
efficient code for <B>final</B> method calls. However, in most cases it
won’t make any overall performance difference in your program, so
it’s best to only use <B>final</B> as a design decision, and not as an
attempt to improve performance.
</backtalk:display>
[ <a href='http://www.mindview.net/backtalk/CommentServlet?ID=TIJ3_CHAPTER7_I13'
target="_blank">Add Comment</a> ]
<backtalk:display ID=TIJ3_CHAPTER7_I14>
</FONT><A NAME="_Toc375545331"></A><A NAME="_Toc481064629"></A><BR></P></DIV>
<A NAME="Heading239"></A><FONT FACE = "Verdana"><H3 ALIGN="LEFT">
Producing the right behavior</H3></FONT>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Once you know that all method binding in
Java happens polymorphically via late binding, you can write your code to talk
to the base class and know that all the derived-class cases will work correctly
using the same code. Or to put it another way, you “send a message to an
object and let the object figure out the right thing to do.”
</backtalk:display>
[ <a href='http://www.mindview.net/backtalk/CommentServlet?ID=TIJ3_CHAPTER7_I14'
target="_blank">Add Comment</a> ]
<backtalk:display ID=TIJ3_CHAPTER7_I15>
</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The classic example in OOP is the
“<A NAME="Index690"></A>shape” example. This is commonly used
because it is easy to visualize, but unfortunately it can confuse novice
programmers into thinking that OOP is just for graphics programming, which is of
course not the case.
</backtalk:display>
[ <a href='http://www.mindview.net/backtalk/CommentServlet?ID=TIJ3_CHAPTER7_I15'
target="_blank">Add Comment</a> ]
<backtalk:display ID=TIJ3_CHAPTER7_I16>
</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The shape example has a base class called
<B>Shape </B>and various derived types: <B>Circle</B>, <B>Square</B>,
<B>Triangle</B>, etc. The reason the example works so well is that it’s
easy to say “a circle is a type of shape” and be understood.<B>
</B>The inheritance diagram shows the relationships:</FONT><BR></P></DIV>
<DIV ALIGN="CENTER"><FONT FACE="Georgia"><IMG SRC="TIJ215.gif"></FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The upcast could occur in a statement as
simple as:</FONT><BR></P></DIV>
<BLOCKQUOTE><FONT SIZE = "+1"><PRE>Shape s = <font color=#0000ff>new</font> Circle();</PRE></FONT></BLOCKQUOTE>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Here, a <B>Circle</B> object is created
and the resulting reference is immediately assigned to a <B>Shape</B>, which
would seem to be an error (assigning one type to another); and yet it’s
fine because a <B>Circle</B> <I>is</I> a <B>Shape</B> by inheritance. So the
compiler agrees with the statement and doesn’t issue an error message.
</backtalk:display>
[ <a href='http://www.mindview.net/backtalk/CommentServlet?ID=TIJ3_CHAPTER7_I16'
target="_blank">Add Comment</a> ]
<backtalk:display ID=TIJ3_CHAPTER7_I17>
</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Suppose you call one of the base-class
methods (that have been overridden in the derived classes):</FONT><BR></P></DIV>
<BLOCKQUOTE><FONT SIZE = "+1"><PRE>s.draw();</PRE></FONT></BLOCKQUOTE>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Again, you might expect that
<B>Shape</B>’s <B>draw( )</B> is called because this is, after all, a
<B>Shape</B> reference—so how could the compiler know to do anything else?
And yet the proper <B>Circle.draw( )</B> is called because of late binding
(polymorphism).
</backtalk:display>
[ <a href='http://www.mindview.net/backtalk/CommentServlet?ID=TIJ3_CHAPTER7_I17'
target="_blank">Add Comment</a> ]
<backtalk:display ID=TIJ3_CHAPTER7_I18>
</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The following example puts it a slightly
different way:</FONT><BR></P></DIV>
<BLOCKQUOTE><FONT SIZE = "+1"><PRE><font color=#009900>//: c07:Shapes.java</font>
<font color=#009900>// Polymorphism in Java.</font>
<font color=#0000ff>class</font> Shape {
<font color=#0000ff>void</font> draw() {}
<font color=#0000ff>void</font> erase() {}
}
<font color=#0000ff>class</font> Circle <font color=#0000ff>extends</font> Shape {
<font color=#0000ff>void</font> draw() {
System.out.println(<font color=#004488>"Circle.draw()"</font>);
}
<font color=#0000ff>void</font> erase() {
System.out.println(<font color=#004488>"Circle.erase()"</font>);
}
}
<font color=#0000ff>class</font> Square <font color=#0000ff>extends</font> Shape {
<font color=#0000ff>void</font> draw() {
System.out.println(<font color=#004488>"Square.draw()"</font>);
}
<font color=#0000ff>void</font> erase() {
System.out.println(<font color=#004488>"Square.erase()"</font>);
}
}
<font color=#0000ff>class</font> Triangle <font color=#0000ff>extends</font> Shape {
<font color=#0000ff>void</font> draw() {
System.out.println(<font color=#004488>"Triangle.draw()"</font>);
}
<font color=#0000ff>void</font> erase() {
System.out.println(<font color=#004488>"Triangle.erase()"</font>);
}
}
<font color=#0000ff>public</font> <font color=#0000ff>class</font> Shapes {
<font color=#0000ff>public</font> <font color=#0000ff>static</font> Shape randShape() {
<font color=#0000ff>switch</font>((<font color=#0000ff>int</font>)(Math.random() * 3)) {
<font color=#0000ff>default</font>:
<font color=#0000ff>case</font> 0: <font color=#0000ff>return</font> <font color=#0000ff>new</font> Circle();
<font color=#0000ff>case</font> 1: <font color=#0000ff>return</font> <font color=#0000ff>new</font> Square();
<font color=#0000ff>case</font> 2: <font color=#0000ff>return</font> <font color=#0000ff>new</font> Triangle();
}
}
<font color=#0000ff>public</font> <font color=#0000ff>static</font> <font color=#0000ff>void</font> main(String[] args) {
Shape[] s = <font color=#0000ff>new</font> Shape[9];
<font color=#009900>// Fill up the array with shapes:</font>
<font color=#0000ff>for</font>(<font color=#0000ff>int</font> i = 0; i < s.length; i++)
s[i] = randShape();
<font color=#009900>// Make polymorphic method calls:</font>
<font color=#0000ff>for</font>(<font color=#0000ff>int</font> i = 0; i < s.length; i++)
s[i].draw();
}
} <font color=#009900>///:~</font></PRE></FONT></BLOCKQUOTE>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The base class <B>Shape</B> establishes
the common interface to anything inherited from <B>Shape</B>—that is, all
shapes can be drawn and erased. The derived classes override these definitions
to provide unique behavior for each specific type of shape.
</backtalk:display>
[ <a href='http://www.mindview.net/backtalk/CommentServlet?ID=TIJ3_CHAPTER7_I18'
target="_blank">Add Comment</a> ]
<backtalk:display ID=TIJ3_CHAPTER7_I19>
</FONT><BR></P></DIV>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -