📄 chapter11.html
字号:
Hashtable h = <font color=#0000ff>new</font> Hashtable();
<font color=#0000ff>for</font>(<font color=#0000ff>int</font> i = 0; i < petTypes.length; i++)
h.put(petTypes[i].toString(),
<font color=#0000ff>new</font> Counter());
<font color=#0000ff>for</font>(<font color=#0000ff>int</font> i = 0; i < pets.size(); i++) {
Object o = pets.elementAt(i);
<font color=#009900>// Using isInstance to eliminate individual</font>
<font color=#009900>// instanceof expressions:</font>
<font color=#0000ff>for</font> (<font color=#0000ff>int</font> j = 0; j < petTypes.length; ++j)
<font color=#0000ff>if</font> (petTypes[j].isInstance(o)) {
String key = petTypes[j].toString();
((Counter)h.get(key)).i++;
}
}
<font color=#0000ff>for</font>(<font color=#0000ff>int</font> i = 0; i < pets.size(); i++)
System.out.println(
pets.elementAt(i).getClass().toString());
Enumeration keys = h.keys();
<font color=#0000ff>while</font>(keys.hasMoreElements()) {
String nm = (String)keys.nextElement();
Counter cnt = (Counter)h.get(nm);
System.out.println(
nm.substring(nm.lastIndexOf('.') + 1) +
<font color=#004488>" quantity: "</font> + cnt.i);
}
}
} <font color=#009900>///:~</font></PRE></FONT></BLOCKQUOTE>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">You can see that the Java
1.1<A NAME="Index1396"></A> <B>isInstance( )</B> method has eliminated the
need for the <B>instanceof</B> expressions. In addition, this means that you can
add new types of pets simply by changing the <B>petTypes</B> array; the rest of
the program does not need modification (as it did when using the
<B>instanceof</B>
expressions).</FONT><A NAME="_Toc305593313"></A><A NAME="_Toc305628785"></A><A NAME="_Toc312374146"></A><A NAME="_Toc375545409"></A><A NAME="_Toc375545407"></A><A NAME="_Toc408018648"></A><BR></P></DIV>
<A NAME="Heading358"></A><FONT FACE = "Verdana"><H2 ALIGN="LEFT">
RTTI syntax</H2></FONT>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Java performs its
<A NAME="Index1397"></A><A NAME="Index1398"></A>RTTI using the <B>Class</B>
object, even if you’re doing something like a cast. The class <B>Class</B>
also has a number of other ways you can use RTTI.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">First, you must get a handle to the
appropriate <B>Class</B> object. One way to do this, as shown in the previous
example, is to use a string and the <B>Class.forName( )</B> method. This is
convenient because you don’t need an object of that type in order to get
the <B>Class</B> handle. However, if you do already have an object of the type
you’re interested in, you can fetch the <B>Class</B> handle by calling a
method that’s part of the <B>Object</B> root class:<B>
<A NAME="Index1399"></A><A NAME="Index1400"></A>getClass( )</B>. This
returns the <B>Class</B> handle representing the actual type of the object.
<B>Class</B> has several interesting and sometimes useful methods, demonstrated
in the following example:</FONT><BR></P></DIV>
<BLOCKQUOTE><FONT SIZE = "+1"><PRE><font color=#009900>//: ToyTest.java</font>
<font color=#009900>// Testing class Class</font>
<font color=#0000ff>interface</font> HasBatteries {}
<font color=#0000ff>interface</font> Waterproof {}
<font color=#0000ff>interface</font> ShootsThings {}
<font color=#0000ff>class</font> Toy {
<font color=#009900>// Comment out the following default</font>
<font color=#009900>// constructor to see </font>
<font color=#009900>// NoSuchMethodError from (*1*)</font>
Toy() {}
Toy(<font color=#0000ff>int</font> i) {}
}
<font color=#0000ff>class</font> FancyToy <font color=#0000ff>extends</font> Toy
<font color=#0000ff>implements</font> HasBatteries,
Waterproof, ShootsThings {
FancyToy() { <font color=#0000ff>super</font>(1); }
}
<font color=#0000ff>public</font> <font color=#0000ff>class</font> ToyTest {
<font color=#0000ff>public</font> <font color=#0000ff>static</font> <font color=#0000ff>void</font> main(String[] args) {
Class c = <font color=#0000ff>null</font>;
<font color=#0000ff>try</font> {
c = Class.forName(<font color=#004488>"FancyToy"</font>);
} <font color=#0000ff>catch</font>(ClassNotFoundException e) {}
printInfo(c);
Class[] faces = c.getInterfaces();
<font color=#0000ff>for</font>(<font color=#0000ff>int</font> i = 0; i < faces.length; i++)
printInfo(faces[i]);
Class cy = c.getSuperclass();
Object o = <font color=#0000ff>null</font>;
<font color=#0000ff>try</font> {
<font color=#009900>// Requires default constructor:</font>
o = cy.newInstance(); <font color=#009900>// (*1*)</font>
} <font color=#0000ff>catch</font>(InstantiationException e) {}
<font color=#0000ff>catch</font>(IllegalAccessException e) {}
printInfo(o.getClass());
}
<font color=#0000ff>static</font> <font color=#0000ff>void</font> printInfo(Class cc) {
System.out.println(
<font color=#004488>"Class name: "</font> + cc.getName() +
<font color=#004488>" is interface? ["</font> +
cc.isInterface() + <font color=#004488>"]"</font>);
}
} <font color=#009900>///:~</font></PRE></FONT></BLOCKQUOTE>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">You can see that <B>class
FancyToy</B> is quite complicated, since it inherits from <B>Toy</B> and
<B>implements</B> the <B>interface</B>s of <B>HasBatteries</B>,
<B>Waterproof</B>, and <B>ShootsThings</B>. In <B>main( )</B>, a
<B>Class</B> handle is created and initialized to the <B>FancyToy</B>
<B>Class</B> using <B>forName( )</B> inside an appropriate <B>try</B>
block.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The
<B>Class.getInterfaces( )</B>
<A NAME="Index1401"></A><A NAME="Index1402"></A>method returns an array of
<B>Class</B> objects representing the interfaces that are contained in the
<B>Class</B> object of interest.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">If you have a <B>Class</B> object
you can also ask it for its direct base class using
<A NAME="Index1403"></A><A NAME="Index1404"></A><B>getSuperclass( )</B>.
This, of course, returns a <B>Class</B> handle that you can further query. This
means that, at run time, you can discover an object’s entire class
hierarchy.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The
<A NAME="Index1405"></A><A NAME="Index1406"></A><B>newInstance( )</B>
method of <B>Class</B> can, at first, seem like just another way to
<B>clone( )</B> an object. However, you can create a new object with
<B>newInstance( )</B> <I>without</I> an existing object, as seen here,
because there is no <B>Toy</B> object, only <B>cy</B>, which is a handle to
<B>y</B>’s <B>Class</B> object. This is a way to implement a
“virtual constructor,” which allows you to say “I don’t
know exactly what type you are, but create yourself properly anyway.” In
the example above, <B>cy</B> is just a <B>Class</B> handle with no further type
information known at compile time. And when you create a new instance, you get
back an <B>Object</B> handle. But that handle is pointing to a <B>Toy</B>
object. Of course, before you can send any messages other than those accepted by
<B>Object</B>, you have to investigate it a bit and do some casting. In
addition, the class that’s being created with <B>newInstance( )</B>
must have a default constructor. There’s no way to use
<B>newInstance( ) </B>to create objects that have non-default constructors,
so this can be a bit limiting in Java 1. However, the <I>reflection</I> API in
Java 1.1<A NAME="Index1407"></A> (discussed in the next section) allows you to
dynamically use any constructor in a class.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The final method in the listing is
<A NAME="Index1408"></A><A NAME="Index1409"></A><B>printInfo( ),</B> which
takes a <B>Class</B> handle and gets its name with
<A NAME="Index1410"></A><A NAME="Index1411"></A><B>getName( ),</B> and
finds out whether it’s an interface with
<A NAME="Index1412"></A><A NAME="Index1413"></A><B>isInterface( )</B>.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The output from this program
is:</FONT><BR></P></DIV>
<BLOCKQUOTE><FONT SIZE = "+1"><PRE>Class name: FancyToy is <font color=#0000ff>interface</font>? [<font color=#0000ff>false</font>]
Class name: HasBatteries is <font color=#0000ff>interface</font>? [<font color=#0000ff>true</font>]
Class name: Waterproof is <font color=#0000ff>interface</font>? [<font color=#0000ff>true</font>]
Class name: ShootsThings is <font color=#0000ff>interface</font>? [<font color=#0000ff>true</font>]
Class name: Toy is <font color=#0000ff>interface</font>? [<font color=#0000ff>false</font>]</PRE></FONT></BLOCKQUOTE>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Thus, with the <B>Class</B> object
you can find out just about everything you want to know about an
object.</FONT><A NAME="_Toc312374147"></A><A NAME="_Toc375545410"></A><A NAME="_Toc408018649"></A><BR></P></DIV>
<A NAME="Heading359"></A><FONT FACE = "Verdana"><H2 ALIGN="LEFT">
Reflection: run-time <BR>class
information<BR><A NAME="Index1414"></A><A NAME="Index1415"></A></H2></FONT>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">If you don’t know the precise
type of an object, RTTI will tell you. However, there’s a limitation: the
type must be known at compile time in order for you to be able to detect it
using RTTI and do something useful with the information. Put another way, the
compiler must know about all the classes you’re working with for
RTTI.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">This doesn’t seem like that
much of a limitation at first, but suppose you’re given a handle to an
object that’s not in your program space. In fact, the class of the object
isn’t even available to your program at compile time. For example, suppose
you get a bunch of bytes from a disk file or from a network connection and
you’re told that those bytes represent a class. Since the compiler
can’t know about the class while it’s compiling the code, how can
you possibly use such a class?</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">In a traditional programming
environment this seems like a far-fetched scenario. But as we move into a larger
programming world there are important cases in which this happens. The first is
component-based programming in which you build projects using
<A NAME="Index1416"></A><I>Rapid Application Development</I> (RAD) in an
application builder tool. This is a visual approach to creating a program (which
you see on the screen as a <I>form</I>) by moving icons that represent
components onto the form. These components are then configured by setting some
of their values at program time. This design-time configuration requires that
any component be instantiable and that it expose some part of itself and allow
its values to be read and set. In addition, components that handle GUI events
must expose information about appropriate methods so that the RAD environment
can assist the programmer in overriding these event-handling methods. Reflection
provides the mechanism to detect the available methods and produce the method
names. <A NAME="Index1417"></A>Java 1.1 provides a structure for component-based
programming through Java Beans (described in Chapter 13).</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Another compelling motivation for
discovering class information at run-time is to provide the ability to create
and execute objects on remote platforms across a network. This is called
<I>Remote Method Invocation</I> (RMI) and it allows a Java program (version 1.1
and higher) to have objects distributed across many machines. This distribution
can happen for a number of reasons: perhaps you’re doing a
computation-intensive task and you want to break it up and put pieces on
machines that are idle in order to speed things up. In some situations you might
want to place code that handles particular types of tasks (e.g. “Business
Rules” in a multi-tier client/server architecture) on a particular machine
so that machine becomes a common repository describing those actions and it can
be easily changed to affect everyone in the system. (This is an interesting
development since the machine exists solely to make software changes easy!)
Along these lines, distributed computing also supports specialized hardware that
might be good at a particular task – matrix inversions, for example
– but inappropriate or too expensive for general purpose
programming.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">In Java
1.1<A NAME="Index1418"></A>, the class <B>Class</B> (described previously in
this chapter) is extended to support the concept of <I>reflection</I>, and
there’s an additional library, <B>java.lang.reflect, </B>with classes
<A NAME="Index1419"></A><A NAME="Index1420"></A><B>Field</B>,
<A NAME="Index1421"></A><A NAME="Index1422"></A><B>Method</B>, and
<A NAME="Index1423"></A><A NAME="Index1424"></A><B>Constructor </B>(each of
which implement the <B>Member interface</B>). Objects of these types are created
by the JVM at run-time to represent the corresponding member in the unknown
class. You can then use the <B>Constructor</B>s to create new objects, the
<B>get( )</B> and <B>set( )</B> methods to read and modify the fields
associated with <B>Field</B> objects, and the <B>invoke( )</B> method to
call a method associated with a <B>Method</B> object. In addition, you can call
the convenience methods <B>getFields( )</B>, <B>getMethods( )</B>,
<B>getConstructors( )</B>, etc., to return arrays of the objects
representing the fields, methods, and constructors. (You can find out more by
looking up the class <B>Class</B> in your online documentation.)<B> </B>Thus,
the class information for anonymous objects can be completely determined at run
time, and nothing need be known at compile time.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">It’s important to realize
that there’s nothing magic about reflection. When you’re using
reflection to interact with an object of an unknown type, the JVM will simply
look at the object and see that it belongs to a particular class (just like
ordinary RTTI) but then, before it can do anything else, the <B>Class</B> object
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -