📄 chapter11.html
字号:
<!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:Tjava14.rtf
Application Directory:c:\TOOLS\RTF2HTML\
Subject:
Author:Bruce Eckel
Operator:Bruce Eckel
Document Comments:
Version Comments:
Comments:
Keywords:
Translation Date:02/04/2000
Translation Time:23:25:26
Translation Platform:Win32
Number of Output files:27
This File:Chapter11.html
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>11: Run-time type identification</TITLE>
</HEAD>
<BODY BGCOLOR="#FFFFFF"><DIV ALIGN="CENTER">
<a href="http://www.MindView.net">
<img src="mindview-head.gif" alt="MindView Inc." BORDER = "0"></a>
<CENTER>
<FONT FACE="Verdana" size = "-1">
[ <a href="README-HTML.txt">Viewing Hints</a> ]
[ <a href="http://www.mindview.net/TIJ2/index.html">2nd Edition</a> ]
[ <a href="http://www.mindview.net/MailingList.html">Free Newsletter</a> ] <br>
[ <a href="http://www.mindview.net/Training.html">Seminars</a> ]
[ <a href="http://www.mindview.net/javaCD2.html">Seminars on CD ROM</a> ]
[ <a href="http://www.mindview.net/CPPServices/#ConsultingServices">Consulting</a> ]
</FONT>
<H2><FONT FACE="Verdana">
Thinking in Java, 1st edition</FONT></H2>
<H3><FONT FACE="Verdana">©1998 by Bruce Eckel</FONT></H3>
<FONT FACE="Verdana" size = "-1">
[ <a href="Chapter10.html">Previous Chapter</a> ]
[ <a href="SimpleContents.html">Short TOC</a> ]
[ <a href="Contents.html">Table of Contents</a> ]
[ <a href="DocIndex.html">Index</a> ]
[ <a href="Chapter12.html">Next Chapter</a> ]
</FONT>
</CENTER>
</P></DIV><A NAME="Chapter_12"></A><A NAME="_Toc375545404"></A><A NAME="_Toc407441455"></A><A NAME="_Toc408018644"></A><A NAME="Heading351"></A><FONT FACE = "Verdana"><H1 ALIGN="LEFT">
11: Run-time type identification</H1></FONT>
<DIV ALIGN="LEFT"><P><FONT FACE="Verdana" SIZE=4>The idea of run-time type
identification (RTTI) seems fairly simple at first: it lets you find the exact
type of an object when you have a handle to only the base type.
</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">However, the <I>need</I> for RTTI
uncovers a whole plethora of interesting (and often perplexing) OO design issues
and raises fundamental questions of how you should structure your programs.
</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">This chapter looks at the ways that
Java allows you to discover information about objects and classes at run-time.
This takes two forms: “traditional” RTTI, which assumes that you
have all the types available at compile-time and run-time, and the
“reflection” mechanism in Java 1.1,<A NAME="Index1352"></A> which
allows you to discover class information solely at run-time. The
“traditional” RTTI will be covered first, followed by a discussion
of
reflection.</FONT><A NAME="_Toc375545405"></A><A NAME="_Toc408018645"></A><BR></P></DIV>
<A NAME="Heading352"></A><FONT FACE = "Verdana"><H2 ALIGN="LEFT">
The need for RTTI</H2></FONT>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Consider the now familiar example
of a class hierarchy that uses polymorphism. The generic type is the base class
<B>Shape<A NAME="Index1353"></A><A NAME="Index1354"></A></B>, and the specific
derived types are <B>Circle</B>, <B>Square</B>, and
<B>Triangle</B>:</FONT><BR></P></DIV>
<DIV ALIGN="CENTER"><FONT FACE="Georgia"><IMG SRC="Tjava118.gif"></FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">This is a typical class hierarchy
diagram, with the base class at the top and the derived classes growing
downward. The normal goal in object-oriented
programming<A NAME="Index1355"></A><A NAME="Index1356"></A> is for the bulk of
your code to manipulate handles to the base type (<B>Shape</B>, in this case),
so if you decide to extend the program by adding a new class (<B>Rhomboid</B>,
derived from <B>Shape</B>, for example), the bulk of the code is not affected.
In this example, the dynamically bound method in the <B>Shape</B> interface is
<B>draw( )</B>, so the intent is for the client programmer to call
<B>draw( )</B> through a generic <B>Shape</B> handle. <B>draw( )</B>
is overridden in all of the derived classes, and because it is a dynamically
bound method, the proper behavior will occur even though it is called through a
generic <B>Shape</B> handle. That’s
<A NAME="Index1357"></A>polymorphism.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Thus, you generally create a
specific object (<B>Circle</B>, <B>Square</B>, or <B>Triangle</B>), upcast it to
a <B>Shape</B> (forgetting the specific type of the object), and use that
anonymous <B>Shape </B>handle in the rest of the program.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">As a brief review of polymorphism
and <A NAME="Index1358"></A>upcasting, you might code the above example as
follows: </FONT><BR></P></DIV>
<BLOCKQUOTE><FONT SIZE = "+1"><PRE><font color=#009900>//: Shapes.java</font>
<font color=#0000ff>package</font> c11;
<font color=#0000ff>import</font> java.util.*;
<font color=#0000ff>interface</font> Shape {
<font color=#0000ff>void</font> draw();
}
<font color=#0000ff>class</font> Circle <font color=#0000ff>implements</font> Shape {
<font color=#0000ff>public</font> <font color=#0000ff>void</font> draw() {
System.out.println(<font color=#004488>"Circle.draw()"</font>);
}
}
<font color=#0000ff>class</font> Square <font color=#0000ff>implements</font> Shape {
<font color=#0000ff>public</font> <font color=#0000ff>void</font> draw() {
System.out.println(<font color=#004488>"Square.draw()"</font>);
}
}
<font color=#0000ff>class</font> Triangle <font color=#0000ff>implements</font> Shape {
<font color=#0000ff>public</font> <font color=#0000ff>void</font> draw() {
System.out.println(<font color=#004488>"Triangle.draw()"</font>);
}
}
<font color=#0000ff>public</font> <font color=#0000ff>class</font> Shapes {
<font color=#0000ff>public</font> <font color=#0000ff>static</font> <font color=#0000ff>void</font> main(String[] args) {
Vector s = <font color=#0000ff>new</font> Vector();
s.addElement(<font color=#0000ff>new</font> Circle());
s.addElement(<font color=#0000ff>new</font> Square());
s.addElement(<font color=#0000ff>new</font> Triangle());
Enumeration e = s.elements();
<font color=#0000ff>while</font>(e.hasMoreElements())
((Shape)e.nextElement()).draw();
}
} <font color=#009900>///:~</font></PRE></FONT></BLOCKQUOTE>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The base class could be coded as an
<B>interface</B>, an <B>abstract</B> class, or an ordinary class. Since <B>Shape
</B>has no concrete members (that is, members with definitions), and it’s
not intended that you ever create a plain <B>Shape</B> object, the most
appropriate and flexible representation is an <B>interface</B>. It’s also
cleaner because you don’t have all those <B>abstract</B> keywords lying
about.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Each of the derived classes
overrides the base-class <B>draw</B> method so it behaves differently. In
<B>main( )</B>, specific types of <B>Shape</B> are created and then added
to a <B>Vector</B>. This is the point at which the upcast occurs because the
<B>Vector</B> holds only <B>Object</B>s. Since everything in Java (with the
exception of primitives) is an <B>Object</B>, a <B>Vector</B> can also hold
<B>Shape</B> objects. But during an upcast to <B>Object</B>,<B> </B>it also
loses any specific information, including the fact that the objects are
<B>shape</B>s. To the <B>Vector</B>, they are just
<B>Object</B>s.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">At the point you fetch an element
out of the <B>Vector</B> with <B>nextElement( )</B>, things get a little
busy. Since <B>Vector</B> holds only <B>Object</B>s, <B>nextElement( )</B>
naturally produces an <B>Object</B> handle. But we know it’s really a
<B>Shape</B> handle, and we want to send <B>Shape</B> messages to that object.
So a <A NAME="Index1359"></A><A NAME="Index1360"></A>cast to <B>Shape </B>is
necessary using the traditional “<B>(Shape)</B>” cast. This is the
most basic form of RTTI, since in Java all casts are checked at run-time for
correctness. That’s exactly what RTTI means: at run-time, the type of an
object is identified.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">In this case, the RTTI cast is only
partial: the <B>Object</B> is cast to a <B>Shape</B>, and not all the way to a
<B>Circle</B>, <B>Square</B>, or <B>Triangle</B>. That’s because the only
thing we <I>know</I> at this point is that the <B>Vector</B> is full of
<B>Shape</B>s. At compile-time, this is enforced only by your own self-imposed
rules, but at run-time the cast ensures it.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Now polymorphism takes over and the
exact method that’s called for the <B>Shape</B> is determined by whether
the handle is for a <B>Circle</B>, <B>Square</B>, or <B>Triangle</B>. And in
general, this is how it should be; you want the bulk of your code to know as
little as possible about <I>specific</I> types of objects, and to just deal with
the general representation of a family of objects (in this case, <B>Shape</B>).
As a result, your code will be easier to write, read, and maintain, and your
designs will be easier to implement, understand, and change. So polymorphism is
the general goal in object-oriented programming.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">But what if you have a special
programming problem that’s easiest to solve if you know the exact type of
a generic handle<A NAME="Index1361"></A><A NAME="Index1362"></A>? For example,
suppose you want to allow your users to highlight all the shapes of any
particular type by turning them purple. This way, they can find all the
triangles on the screen by highlighting them. This is what RTTI accomplishes:
you can ask a handle to a <B>Shape</B> exactly what type it’s referring
to.</FONT><A NAME="_Toc312374135"></A><A NAME="_Toc375545406"></A><A NAME="_Toc408018646"></A><BR></P></DIV>
<A NAME="Heading353"></A><FONT FACE = "Verdana"><H3 ALIGN="LEFT">
The Class object</H3></FONT>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">To understand how RTTI works in
Java, you must first know how type information is represented at run time. This
is accomplished through a special kind of object called the
<A NAME="Index1363"></A><A NAME="Index1364"></A><A NAME="Index1365"></A><I>Class
object,</I> which contains information about the class. (This is sometimes
called a <A NAME="Index1366"></A><A NAME="Index1367"></A><I>meta-class.</I>) In
fact, the <B>Class</B> object is used to create all of the “regular”
objects of your class.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">There’s a <B>Class</B> object
for each class that is part of your program. That is, each time you write a new
class, a single <B>Class</B> object is also created (and stored, appropriately
enough, in an identically named <B>.class </B>file). At run time, when you want
to make an object of that class, the
<A NAME="Index1368"></A><A NAME="Index1369"></A>Java Virtual Machine (JVM)
that’s executing your program first checks to see if the <B>Class</B>
object for that type is loaded. If not, the JVM loads it by finding the
<B>.class </B>file with that name. Thus, a Java program isn’t completely
loaded before it begins, which is different from many traditional
languages.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Once the <B>Class</B> object for
that type is in memory, it is used to create all objects of that
type.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">If this seems shadowy or if you
don’t really believe it, here’s a demonstration program to prove
it:</FONT><BR></P></DIV>
<BLOCKQUOTE><FONT SIZE = "+1"><PRE><font color=#009900>//: SweetShop.java</font>
<font color=#009900>// Examination of the way the class loader works</font>
<font color=#0000ff>class</font> Candy {
<font color=#0000ff>static</font> {
System.out.println(<font color=#004488>"Loading Candy"</font>);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -