📄 tij0179.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="tij0178.html">Prev</a> | <a href="tij0180.html">Next</a>
</td>
</tr></table>
<hr>
<H2 ALIGN=LEFT>
Multiple
dispatching
</H2>
<DIV ALIGN=LEFT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">The
above design is certainly satisfactory. Adding new types to the system consists
of adding or modifying distinct classes without causing code changes to be
propagated throughout the system. In addition, RTTI is not
“misused” as it was in
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>RecycleA.java</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">.
However, it’s possible to go one step further and take a purist viewpoint
about <A NAME="Index2990"></A>RTTI
and say that it should be eliminated altogether from the operation of sorting
the trash into bins.
</FONT><P></DIV><DIV ALIGN=LEFT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">To
accomplish this, you must first take the perspective that all type-dependent
activities – such as detecting the type of a piece of trash and putting
it into the appropriate bin – should be controlled through polymorphism
and dynamic binding.
</FONT><P></DIV><DIV ALIGN=LEFT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">The
previous examples first sorted by type, then acted on sequences of elements
that were all of a particular type. But whenever you find yourself picking out
particular types, stop and think. The whole idea of polymorphism
(dynamically-bound method calls) is to handle type-specific information for
you. So why are you hunting for types?
</FONT><P></DIV><DIV ALIGN=LEFT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">The
answer is something you probably don’t think about: Java performs only
single dispatching. That is, if you are performing an operation on more than
one object whose type is unknown, Java will invoke the dynamic binding
mechanism on only one of those types. This doesn’t solve the problem, so
you end up detecting some types manually and effectively producing your own
dynamic binding behavior.
</FONT><P></DIV><DIV ALIGN=LEFT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">The
solution is called <A NAME="Index2991"></A><A NAME="Index2992"></A></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><I>multiple
dispatching
</I></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">,
which means setting up a configuration such that a single method call produces
more than one dynamic method call and thus determines more than one type in the
process. To get this effect, you need to work with more than one type
hierarchy: you’ll need a type hierarchy for each dispatch. The following
example works with two hierarchies: the existing
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>Trash</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
family and a hierarchy of the types of trash bins that the trash will be placed
into. This second hierarchy isn’t always obvious and in this case it
needed to be created in order to produce multiple dispatching (in this case
there will be only two dispatches, which is referred to as <A NAME="Index2993"></A><A NAME="Index2994"></A></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><I>double
dispatching
</I></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">).</FONT><a name="_Toc408018804"></a><P></DIV>
<A NAME="Heading565"></A><H3 ALIGN=LEFT>
Implementing
the double dispatch
</H3>
<DIV ALIGN=LEFT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">Remember
that polymorphism can occur only via method calls, so if you want double
dispatching to occur, there must be two method calls: one used to determine the
type within each hierarchy. In the
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>Trash</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
hierarchy there will be a new method called
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>addToBin( )</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">,
which takes an argument of an array of
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>TypedBin</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">.
It uses this array to step through and try to add itself to the appropriate
bin, and this is where you’ll see the double dispatch.
</FONT><P></DIV><DIV ALIGN=LEFT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">The
new hierarchy is
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>TypedBin</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">,
and it contains its own method called
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>add( )</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
that is also used polymorphically. But here’s an additional twist:
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>add( )</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
is
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><I>overloaded</I></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
to take arguments of the different types of trash. So an essential part of the
double dispatching scheme also involves overloading.
</FONT><P></DIV><DIV ALIGN=LEFT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">Redesigning
the program produces a dilemma: it’s now necessary for the base class
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>Trash</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
to contain an
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>addToBin( )</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
method. One approach is to copy all of the code and change the base class.
Another approach, which you can take when you don’t have control of the
source code, is to put the
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>addToBin( )</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
method into an
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>interface</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">,
leave
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>Trash</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
alone, and inherit new specific types of
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>Aluminum</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">,
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>Paper</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">,
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>Glass</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">,
and
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>Cardboard</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">.
This is the approach that will be taken here.
</FONT><P></DIV><DIV ALIGN=LEFT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">Most
of the classes in this design must be
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>public</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">,
so they are placed in their own files. Here’s the interface:
</FONT><P></DIV>
<font color="#990000"><PRE><font color="#009900">//: TypedBinMember.java</font>
<font color="#009900">// An interface for adding the double dispatching</font>
<font color="#009900">// method to the trash hierarchy without </font>
<font color="#009900">// modifying the original hierarchy.</font>
<font color="#0000ff">package</font> c16.doubledispatch;
<font color="#0000ff">interface</font> TypedBinMember {
<font color="#009900">// The new method:</font>
<font color="#0000ff">boolean</font> addToBin(TypedBin[] tb);
} <font color="#009900">///:~ </PRE></font></font><DIV ALIGN=LEFT><P></DIV><DIV ALIGN=LEFT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">In
each particular subtype of
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>Aluminum</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">,
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>Paper</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">,
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>Glass,</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
and
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>Cardboard</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">,
the
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>addToBin( )</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
method in the
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>interface
TypedBinMember
</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
is implemented,, but it
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><I>looks</I></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
like the code is exactly the same in each case:
</FONT><P></DIV>
<font color="#990000"><PRE><font color="#009900">//: DDAluminum.java</font>
<font color="#009900">// Aluminum for double dispatching</font>
<font color="#0000ff">package</font> c16.doubledispatch;
<font color="#0000ff">import</font> c16.trash.*;
<font color="#0000ff">public</font> <font color="#0000ff">class</font> DDAluminum <font color="#0000ff">extends</font> Aluminum
<font color="#0000ff">implements</font> TypedBinMember {
<font color="#0000ff">public</font> DDAluminum(<font color="#0000ff">double</font> wt) { <font color="#0000ff">super</font>(wt); }
<font color="#0000ff">public</font> <font color="#0000ff">boolean</font> addToBin(TypedBin[] tb) {
<font color="#0000ff">for</font>(<font color="#0000ff">int</font> i = 0; i < tb.length; i++)
<font color="#0000ff">if</font>(tb[i].add(<font color="#0000ff">this</font>))
<font color="#0000ff">return</font> <font color="#0000ff">true</font>;
<font color="#0000ff">return</font> <font color="#0000ff">false</font>;
}
} <font color="#009900">///:~ </PRE></font></font><DIV ALIGN=LEFT><P></DIV>
<font color="#990000"><PRE><font color="#009900">//: DDPaper.java</font>
<font color="#009900">// Paper for double dispatching</font>
<font color="#0000ff">package</font> c16.doubledispatch;
<font color="#0000ff">import</font> c16.trash.*;
<font color="#0000ff">public</font> <font color="#0000ff">class</font> DDPaper <font color="#0000ff">extends</font> Paper
<font color="#0000ff">implements</font> TypedBinMember {
<font color="#0000ff">public</font> DDPaper(<font color="#0000ff">double</font> wt) { <font color="#0000ff">super</font>(wt); }
<font color="#0000ff">public</font> <font color="#0000ff">boolean</font> addToBin(TypedBin[] tb) {
<font color="#0000ff">for</font>(<font color="#0000ff">int</font> i = 0; i < tb.length; i++)
<font color="#0000ff">if</font>(tb[i].add(<font color="#0000ff">this</font>))
<font color="#0000ff">return</font> <font color="#0000ff">true</font>;
<font color="#0000ff">return</font> <font color="#0000ff">false</font>;
}
} <font color="#009900">///:~ </PRE></font></font><DIV ALIGN=LEFT><P></DIV>
<font color="#990000"><PRE><font color="#009900">//: DDGlass.java</font>
<font color="#009900">// Glass for double dispatching</font>
<font color="#0000ff">package</font> c16.doubledispatch;
<font color="#0000ff">import</font> c16.trash.*;
<font color="#0000ff">public</font> <font color="#0000ff">class</font> DDGlass <font color="#0000ff">extends</font> Glass
<font color="#0000ff">implements</font> TypedBinMember {
<font color="#0000ff">public</font> DDGlass(<font color="#0000ff">double</font> wt) { <font color="#0000ff">super</font>(wt); }
<font color="#0000ff">public</font> <font color="#0000ff">boolean</font> addToBin(TypedBin[] tb) {
<font color="#0000ff">for</font>(<font color="#0000ff">int</font> i = 0; i < tb.length; i++)
<font color="#0000ff">if</font>(tb[i].add(<font color="#0000ff">this</font>))
<font color="#0000ff">return</font> <font color="#0000ff">true</font>;
<font color="#0000ff">return</font> <font color="#0000ff">false</font>;
}
} <font color="#009900">///:~ </PRE></font></font><DIV ALIGN=LEFT><P></DIV>
<font color="#990000"><PRE><font color="#009900">//: DDCardboard.java</font>
<font color="#009900">// Cardboard for double dispatching</font>
<font color="#0000ff">package</font> c16.doubledispatch;
<font color="#0000ff">import</font> c16.trash.*;
<font color="#0000ff">public</font> <font color="#0000ff">class</font> DDCardboard <font color="#0000ff">extends</font> Cardboard
<font color="#0000ff">implements</font> TypedBinMember {
<font color="#0000ff">public</font> DDCardboard(<font color="#0000ff">double</font> wt) { <font color="#0000ff">super</font>(wt); }
<font color="#0000ff">public</font> <font color="#0000ff">boolean</font> addToBin(TypedBin[] tb) {
<font color="#0000ff">for</font>(<font color="#0000ff">int</font> i = 0; i < tb.length; i++)
<font color="#0000ff">if</font>(tb[i].add(<font color="#0000ff">this</font>))
<font color="#0000ff">return</font> <font color="#0000ff">true</font>;
<font color="#0000ff">return</font> <font color="#0000ff">false</font>;
}
} <font color="#009900">///:~ </PRE></font></font><DIV ALIGN=LEFT><P></DIV><DIV ALIGN=LEFT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">The
code in each
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>addToBin( )
</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">calls
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>add( )</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
for each
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>TypedBin</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
object in the array. But notice the argument:
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>this</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">.
The type of
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>this</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
is different for each subclass of
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>Trash</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">,
so the code is different. (Although this code will benefit if a <A NAME="Index2995"></A><A NAME="Index2996"></A>parameterized
type mechanism is ever added to Java.) So this is the first part of the double
dispatch, because once you’re inside this method you know you’re
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>Aluminum</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">,
or
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>Paper</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">,
etc. During the call to
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>add( )</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">,
this information is passed via the type of
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>this</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">.
The compiler resolves the call to the proper overloaded version of
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>add( )</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">.
But
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -