📄 tij0077.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="tij0076.html">Prev</a> | <a href="tij0078.html">Next</a>
</td>
</tr></table>
<hr>
<H2 ALIGN=LEFT>
The
twist
</H2>
<DIV ALIGN=LEFT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">The
difficulty with
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>Music</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">.</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>java</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
can be seen by running the program. The output is
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>Wind.play( )</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">.
This is clearly the desired output, but it doesn’t seem to make sense
that it would work that way. Look at the
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>tune( )</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
method:
</FONT><P></DIV>
<font color="#990000"><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.middleC);
} </PRE></font><DIV ALIGN=LEFT><P></DIV><DIV ALIGN=LEFT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">It
receives an
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>Instrument</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
handle. So how can the compiler possibly know that this
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>Instrument</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
handle points to a
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>Wind</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
in this case and not a
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>Brass
</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">or
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>Stringed</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">?
The compiler can’t. To get a deeper understanding of the issue,
it’s useful to examine the subject of
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><I>binding</I></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">.</FONT><a name="_Toc312374042"></a><a name="_Toc375545330"></a><a name="_Toc408018533"></a><P></DIV>
<A NAME="Heading211"></A><H3 ALIGN=LEFT>
Method
call binding
<P><A NAME="Index562"></A><A NAME="Index563"></A></H3>
<DIV ALIGN=LEFT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">Connecting
a method call to a method body is called
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><I>binding</I></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">.
When binding is performed before the program is run (by the compiler and
linker, if there is one), it’s called
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><I>early
binding<A NAME="Index564"></A></I></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">.
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.
</FONT><P></DIV><DIV ALIGN=LEFT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">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
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>Instrument</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
handle.
</FONT><P></DIV><DIV ALIGN=LEFT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">The
solution is called
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><I>late
binding<A NAME="Index565"></A><A NAME="Index566"></A></I></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">,
which means that the binding occurs at run-time based on the type of object.
Late binding is also called
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><I>dynamic
binding<A NAME="Index567"></A><A NAME="Index568"></A></I></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
or
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><I>run-time
binding<A NAME="Index569"></A><A NAME="Index570"></A></I></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">.
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.
</FONT><P></DIV><DIV ALIGN=LEFT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">All
method binding in Java uses late binding unless a method has been declared <A NAME="Index571"></A><A NAME="Index572"></A></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>final</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">.
This means that you ordinarily don’t need to make any decisions about
whether late binding will occur – it happens automatically.
</FONT><P></DIV><DIV ALIGN=LEFT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">Why
would you declare a method
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>final</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">?
As noted in the last chapter, it prevents anyone from overriding that method.
Perhaps more importantly, it effectively “turns off” dynamic
binding, or rather it tells the compiler that dynamic binding isn’t
necessary. This allows the compiler to generate more efficient code for
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>final</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
method calls.
</FONT><a name="_Toc375545331"></a><a name="_Toc408018534"></a><P></DIV>
<A NAME="Heading212"></A><H3 ALIGN=LEFT>
Producing
the right behavior
</H3>
<DIV ALIGN=LEFT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">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.”
</FONT><P></DIV><DIV ALIGN=LEFT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">The
classic example in OOP is the “<A NAME="Index573"></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.
</FONT><P></DIV><DIV ALIGN=LEFT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">The
shape example has a base class called
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>Shape
</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">and
various derived types:
</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"><B>Square</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">,
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>Triangle</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">,
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.
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>
</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">The
inheritance diagram shows the relationships:
</FONT><P></DIV><DIV ALIGN=LEFT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">The
upcast could occur in a statement as simple as:
</FONT><P></DIV><DIV ALIGN=LEFT><TT><FONT FACE="Courier New" SIZE=3 COLOR="Black">Shape
s = new Circle();
</FONT></TT><P></DIV><DIV ALIGN=LEFT><P></DIV><DIV ALIGN=LEFT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">Here,
a
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>Circle</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
object is created and the resulting handle is immediately assigned 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">,
which would seem to be an error (assigning one type to another) and yet
it’s fine because 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">
by inheritance. So the compiler agrees with the statement and doesn’t
issue an error message.
</FONT><P></DIV><DIV ALIGN=LEFT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">When
you call one of the base class methods (that have been overridden in the
derived classes):
</FONT><P></DIV><DIV ALIGN=LEFT><TT><FONT FACE="Courier New" SIZE=3 COLOR="Black">s.draw();</FONT></TT><P></DIV><DIV ALIGN=LEFT><P></DIV><DIV ALIGN=LEFT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">Again,
you might expect that
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>Shape</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">’s
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>draw( )</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
is called because this is, after all, 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, so how could the compiler know to do anything else? And yet the proper
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>Circle.draw( )</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
is called because of late binding (polymorphism).
</FONT><P></DIV><DIV ALIGN=LEFT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">The
following example puts it a slightly different way:
</FONT><P></DIV>
<font color="#990000"><PRE><font color="#009900">//: 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("Circle.draw()");
}
<font color="#0000ff">void</font> erase() {
System.out.println("Circle.erase()");
}
}
<font color="#0000ff">class</font> Square <font color="#0000ff">extends</font> Shape {
<font color="#0000ff">void</font> draw() {
System.out.println("Square.draw()");
}
<font color="#0000ff">void</font> erase() {
System.out.println("Square.erase()");
}
}
<font color="#0000ff">class</font> Triangle <font color="#0000ff">extends</font> Shape {
<font color="#0000ff">void</font> draw() {
System.out.println("Triangle.draw()");
}
<font color="#0000ff">void</font> erase() {
System.out.println("Triangle.erase()");
}
}
<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="#009900">// To quiet the compiler</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++)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -