📄 tij312.htm
字号:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html lang="en">
<!--
This document was converted from RTF source:
By r2net 5.8 r2netcmd Windows
See http://www.logictran.com
-->
<head><meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
<title>Thinking in Java, 3rd ed. Revision 4.0: 10: Detecting Types</title>
<link rel="stylesheet" href="stylesheet.css" type="text/css"></head>
<body >
<CENTER> <a href="http://www.MindView.net"> <img src="mindview.gif" alt="MindView Inc." BORDER = "0"></a> <Font FACE="Verdana, Tahoma, Arial, Helvetica, Sans"> <h2>Thinking in Java, 3<sup>rd</sup> ed. Revision 4.0</h2> <FONT size = "-1"><br> [ <a href="README.txt">Viewing Hints</a> ] [ <a href="http://www.mindview.net/Books/TIJ/">Book Home Page</a> ] [ <a href="http://www.mindview.net/Etc/MailingList.html">Free Newsletter</a> ] <br> [ <a href="http://www.mindview.net/Seminars">Seminars</a> ] [ <a href="http://www.mindview.net/CDs">Seminars on CD ROM</a> ] [ <a href="http://www.mindview.net/Services">Consulting</a> ] <br><br> </FONT></FONT> </CENTER>
<font face="Georgia"><div align="CENTER"><a href="TIJ311.htm" target="RightFrame"><img src="./prev.gif" alt="Previous " border="0"></a>
<a href="TIJ313.htm" target="RightFrame"><img src="./next.gif" alt="Next " border="0"></a>
<a href="TIJ3_t.htm"><img src="./first.gif" alt="Title Page " border="0"></a>
<a href="TIJ3_i.htm"><img src="./index.gif" alt="Index " border="0"></a>
<a href="TIJ3_c.htm"><img src="./contents.gif" alt="Contents " border="0"></a>
</div>
<hr>
<h1>
<a name="_Toc24272649"></a><a name="_Toc24775724"></a><a name="Heading9461"></a>10:
Detecting Types</h1>
<p class="Intro">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 only a reference to the base type. <br></p>
<p>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 size="-2"><a href="mailto:TIJ3@MindView.net?Subject=[TIJ3]Chap12_1828" title="Send BackTalk Comment">Feedback</a></font><br></p>
<p>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, 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 size="-2"><a href="mailto:TIJ3@MindView.net?Subject=[TIJ3]Chap12_1829" title="Send BackTalk Comment">Feedback</a></font><br></p>
<h2>
<a name="_Toc375545405"></a><a name="_Toc24775725"></a><a name="Heading9465"></a>The
need for RTTI</h2>
<p>Consider the now familiar example of a class hierarchy that uses polymorphism. The generic type is the base class <b>Shape</b>, and the specific derived types are <a name="Index879"></a><a name="Index880"></a><b>Circle</b>, <b>Square</b>, and <b>Triangle</b>:<br></p>
<p align="center"><img src="TIJ324.png" alt="TIJ324.png" border="0" ><br></p>
<p>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 is for your code to manipulate references to the base type (<a name="Index881"></a><a name="Index882"></a><b>Shape</b>, in this case), so if you decide to extend the program by adding a new class (such as <b>Rhomboid</b>, derived from <b>Shape</b>), 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> reference. In all of the derived classes, <b>draw( )</b> is overridden, and because it is a dynamically bound method, the proper behavior will occur even though it is called through a generic <b>Shape</b> reference. That’s polymorphism. <font size="-2"><a href="mailto:TIJ3@MindView.net?Subject=[TIJ3]Chap12_1830" title="Send BackTalk Comment">Feedback</a></font><br></p>
<p><a name="Index883"></a>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>reference in the rest of the program. <font size="-2"><a href="mailto:TIJ3@MindView.net?Subject=[TIJ3]Chap12_1831" title="Send BackTalk Comment">Feedback</a></font><br></p>
<p>As a brief review of polymorphism and upcasting, you might code the preceding example as follows:<br></p>
<BLOCKQUOTE><FONT SIZE = "+1"><PRE><font color=#009900>//: c10:Shapes.java</font>
<font color=#0000ff>import</font> com.bruceeckel.simpletest.*;
<font color=#0000ff>class</font> Shape {
<font color=#0000ff>void</font> draw() { System.out.println(<font color=#0000ff>this</font> + <font color=#004488>".draw()"</font>); }
}
<font color=#0000ff>class</font> Circle <font color=#0000ff>extends</font> Shape {
<font color=#0000ff>public</font> String toString() { <font color=#0000ff>return</font> <font color=#004488>"Circle"</font>; }
}
<font color=#0000ff>class</font> Square <font color=#0000ff>extends</font> Shape {
<font color=#0000ff>public</font> String toString() { <font color=#0000ff>return</font> <font color=#004488>"Square"</font>; }
}
<font color=#0000ff>class</font> Triangle <font color=#0000ff>extends</font> Shape {
<font color=#0000ff>public</font> String toString() { <font color=#0000ff>return</font> <font color=#004488>"Triangle"</font>; }
}
<font color=#0000ff>public</font> <font color=#0000ff>class</font> Shapes {
<font color=#0000ff>private</font> <font color=#0000ff>static</font> Test monitor = <font color=#0000ff>new</font> Test();
<font color=#0000ff>public</font> <font color=#0000ff>static</font> <font color=#0000ff>void</font> main(String[] args) {
<font color=#009900>// Array of Object, not Shape:</font>
Object[] shapeList = {
<font color=#0000ff>new</font> Circle(),
<font color=#0000ff>new</font> Square(),
<font color=#0000ff>new</font> Triangle()
};
<font color=#0000ff>for</font>(<font color=#0000ff>int</font> i = 0; i < shapeList.length; i++)
((Shape)shapeList[i]).draw(); <font color=#009900>// Must cast</font>
monitor.expect(<font color=#0000ff>new</font> String[] {
<font color=#004488>"Circle.draw()"</font>,
<font color=#004488>"Square.draw()"</font>,
<font color=#004488>"Triangle.draw()"</font>
});
}
} <font color=#009900>///:~</font></PRE></FONT></BLOCKQUOTE><p><br></p>
<p>The base class contains a <b>draw( )</b> method that indirectly uses <b>toString( ) </b>to print an identifier for the class by passing <b>this</b> to <b>System.out.println( )</b>. If that method sees an object, it automatically calls the <b>toString( )</b> method to produce a <b>String</b> representation. Each of the derived classes overrides the <b>toString( )</b> method (from <b>Object</b>) so that <b>draw( )</b> ends up (polymorphically) printing something different in each case. <font size="-2"><a href="mailto:TIJ3@MindView.net?Subject=[TIJ3]Chap12_1832" title="Send BackTalk Comment">Feedback</a></font><br></p>
<p>In <b>main( )</b>, specific types of <b>Shape</b> are created and added to an array. This array is a bit odd because it isn’t an array of <b>Shape </b>(although it could be), but instead an array of the root class <b>Object</b>. The reason for this is to start preparing you for Chapter 11, which presents tools called <i>collections</i> (also called <i>containers</i>), whose sole job is to hold and manage other objects for you. However, to be generally useful these collections need to hold anything. Therefore they hold <b>Object</b>s. So an array of <b>Object</b> will demonstrate an important issue that you will encounter in the Chapter 11 collections. <font size="-2"><a href="mailto:TIJ3@MindView.net?Subject=[TIJ3]A0479" title="Send BackTalk Comment">Feedback</a></font><br></p>
<p>In this example, the upcast occurs when the shape is placed in the array of <b>Object</b>s. Since everything in Java (with the exception of primitives) is an <b>Object</b>, an array of <b>Object</b>s<b> </b>can also hold <b>Shape</b> objects. But during the upcast to <b>Object</b>,<b> </b>the fact is lost that the objects are <b>Shape</b>s. To the array, they are just <b>Object</b>s. <font size="-2"><a href="mailto:TIJ3@MindView.net?Subject=[TIJ3]Chap12_1833" title="Send BackTalk Comment">Feedback</a></font><br></p>
<p>At the point that you fetch an element out of the array with the index operator, things get a little busy. Since the array holds only <b>Object</b>s, indexing naturally produces an <b>Object </b>reference. But we know it’s really a <b>Shape</b> reference, and we want to send <b>Shape</b> messages to that object. So a cast to <a name="Index885"></a><a name="Index886"></a><b>Shape </b>is necessary using the traditional “<b>(Shape)</b>” cast. This is the most basic form of RTTI, because 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 size="-2"><a href="mailto:TIJ3@MindView.net?Subject=[TIJ3]Chap12_1834" title="Send BackTalk Comment">Feedback</a></font><br></p>
<p>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 array 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 size="-2"><a href="mailto:TIJ3@MindView.net?Subject=[TIJ3]Chap12_1835" title="Send BackTalk Comment">Feedback</a></font><br></p>
<p>Now polymorphism takes over and the exact code that’s executed for the <b>Shape</b> is determined by whether the reference 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 a general goal in object-oriented programming. <font size="-2"><a href="mailto:TIJ3@MindView.net?Subject=[TIJ3]Chap12_1836" title="Send BackTalk Comment">Feedback</a></font><br></p>
<p>But what if you have a special programming problem that’s easiest to solve if you know the exact type of a generic reference? 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. Or perhaps your method needs to “rotate” a list of shapes, but it makes no sense to rotate a circle so you’d like to skip only the circle, objects. With RTTI, you can ask a <a name="Index887"></a><a name="Index888"></a><b>Shape</b> reference the exact type that it’s referring to, and thus select and isolate special cases. <font size="-2"><a href="mailto:TIJ3@MindView.net?Subject=[TIJ3]Chap12_1837" title="Send BackTalk Comment">Feedback</a></font><br></p>
<h3>
<a name="_Toc312374135"></a><a name="_Toc375545406"></a><a name="_Toc24775726"></a><a name="Heading9516"></a>The
<b>Class</b> object</h3>
<p>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="Index889"></a><a name="Index890"></a><a name="Index891"></a><i>Class object,</i> which contains information about the class. In fact, the <b>Class</b> object is used to create all of the “regular” objects of your class. <font size="-2"><a href="mailto:TIJ3@MindView.net?Subject=[TIJ3]Chap12_1838" title="Send BackTalk Comment">Feedback</a></font><br></p>
<p>There’s a <b>Class</b> object for each class that is part of your program. That is, each time you write and compile 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 Java Virtual Machine (JVM) that’s executing your program first checks to see if the <a name="Index892"></a><a name="Index893"></a><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 size="-2"><a href="mailto:TIJ3@MindView.net?Subject=[TIJ3]Chap12_1839" title="Send BackTalk Comment">Feedback</a></font><br></p>
<p>Once the <b>Class</b> object for that type is in memory, it is used to create all objects of that type. If this seems shadowy or if you don’t really believe it, here’s a demonstration program to prove it: <font size="-2"><a href="mailto:TIJ3@MindView.net?Subject=[TIJ3]Chap12_1840" title="Send BackTalk Comment">Feedback</a></font><br></p>
<BLOCKQUOTE><FONT SIZE = "+1"><PRE><font color=#009900>//: c10:SweetShop.java</font>
<font color=#009900>// Examination of the way the class loader works.</font>
<font color=#0000ff>import</font> com.bruceeckel.simpletest.*;
<font color=#0000ff>class</font> Candy {
<font color=#0000ff>static</font> {
System.out.println(<font color=#004488>"Loading Candy"</font>);
}
}
<font color=#0000ff>class</font> Gum {
<font color=#0000ff>static</font> {
System.out.println(<font color=#004488>"Loading Gum"</font>);
}
}
<font color=#0000ff>class</font> Cookie {
<font color=#0000ff>static</font> {
System.out.println(<font color=#004488>"Loading Cookie"</font>);
}
}
<font color=#0000ff>public</font> <font color=#0000ff>class</font> SweetShop {
<font color=#0000ff>private</font> <font color=#0000ff>static</font> Test monitor = <font color=#0000ff>new</font> Test();
<font color=#0000ff>public</font> <font color=#0000ff>static</font> <font color=#0000ff>void</font> main(String[] args) {
System.out.println(<font color=#004488>"inside main"</font>);
<font color=#0000ff>new</font> Candy();
System.out.println(<font color=#004488>"After creating Candy"</font>);
<font color=#0000ff>try</font> {
Class.forName(<font color=#004488>"Gum"</font>);
} <font color=#0000ff>catch</font>(ClassNotFoundException e) {
System.out.println(<font color=#004488>"Couldn't find Gum"</font>);
}
System.out.println(<font color=#004488>"After Class.forName(\"</font>Gum\<font color=#004488>")"</font>);
<font color=#0000ff>new</font> Cookie();
System.out.println(<font color=#004488>"After creating Cookie"</font>);
monitor.expect(<font color=#0000ff>new</font> String[] {
<font color=#004488>"inside main"</font>,
<font color=#004488>"Loading Candy"</font>,
<font color=#004488>"After creating Candy"</font>,
<font color=#004488>"Loading Gum"</font>,
<font color=#004488>"After Class.forName(\"</font>Gum\<font color=#004488>")"</font>,
<font color=#004488>"Loading Cookie"</font>,
<font color=#004488>"After creating Cookie"</font>
});
}
} <font color=#009900>///:~</font></PRE></FONT></BLOCKQUOTE><p><br></p>
<p>Each of the classes <b>Candy</b>, <b>Gum</b>, and <b>Cookie</b> have a <a name="Index894"></a><b>static</b> clause that is executed as the class is loaded for the first time. Information will be printed to tell you when loading occurs for that class. In <b>main( )</b>, the object creations are spread out between print statements to help detect the time of loading. <font size="-2"><a href="mailto:TIJ3@MindView.net?Subject=[TIJ3]Chap12_1841" title="Send BackTalk Comment">Feedback</a></font><br></p>
<p>You can see from the output that each <b>Class</b> object is loaded only when it’s needed, and the <b>static</b> initialization is performed upon class loading. <font size="-2"><a href="mailto:TIJ3@MindView.net?Subject=[TIJ3]Chap12_1843" title="Send BackTalk Comment">Feedback</a></font><br></p>
<p>A particularly interesting line is:<br></p>
<BLOCKQUOTE><FONT SIZE = "+1"><PRE>Class.forName(<font color=#004488>"Gum"</font>);</PRE></FONT></BLOCKQUOTE><p><br></p>
<p>This method is a <b>static</b> member of <b>Class</b> (to which all <b>Class</b> objects belong). A <b>Class</b> object is like any other object, so you can get and manipulate a reference to it (that’s what the loader does). One of the ways to get a reference to the <b>Class</b> object is <a name="Index895"></a><a name="Index896"></a><b>forName( )</b>, which takes a <b>String</b> containing the textual name (watch the spelling and capitalization!) of the particular class you want a reference for. It returns a <b>Class</b> reference, which is being ignored here; the call to <b>forName( ) </b>is being made for its side effect, which is to load the class <b>Gum</b> if it isn’t already loaded. In the process of loading, <b>Gum</b>’s <b>static</b> clause is executed. <font size="-2"><a href="mailto:TIJ3@MindView.net?Subject=[TIJ3]Chap12_1842" title="Send BackTalk Comment">Feedback</a></font><br></p>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -