Thinking in Java 3rd Edition
g12544 1 g20041 g1861 17 g20041 www.wgqqh.com/shhgs/tij.html email:shhgs@sohu.com
10,检测类型
初看起来“运行时类型识别(run-time type identification,缩写为
RTTI)”的想法很简单:要让你在只持有这个对象的基类的reference的情况下,找出它的确切的类型。
但是,这种对“RTTI的强烈需求”揭示了许多OO设计中会碰到的有趣
(同时也很令人困惑)的问题,并且引出了“该如何组织程序结构”这一根本性的问题。
本章要讲解Java所提供的,让你在运行时发现对象和类的信息的方法。
它有两种形式:一种是假设你在编译和运行时都完全知道类型的具体信息的“传统的”RTTI,另一种是允许你只依靠运行时信息发掘类型信息的
“reflection”机制。我们先讲“传统的”RTTI,再讨g16782reflection。
为什么会需要RTTI
先想一想我们现在g5062g13475很g10099g5725的g18039个多g5589性的g1375g4388。g17902g11004的基类Shape
g8978g10995出具体的Circle,Square和Triangle类,
这是一个g1868型的类g13007(class hierarchy)结构g3282,基类在g7380g990g19766,g8978g10995类在下g19766。OOP的g11458的g4613是要让你g11004基类的reference(这g18336g4613是Shape)
来写程序,g3252g8504如g7536你g5460程序g18336g19766g2164了一个g7044的类(g8616如Shapeg2460g8978g10995
了一个Rhomboid),g13489g3835多g6980的g1207g11733根本g993会g2475到g5445g2721。在这个g1375g4388
g18336g19766,Shapeg6521g2487g2172g5589g13477g4462的是draw( ),g1122是g4470g6155程序g2604g14033g3827g17902g17819
g17902g11004的Shape reference来g16855g11004draw( )。所有g8978g10995类都会g16218写
draw( ),g13792且g11013g1122这个方法是g2172g5589g13477g4462的,g3252g8504g2375g1427是g17902g17819g17902g11004的
Shape reference来g17839行的g16855g11004,也g14033g5483到g8503确的行为。这g4613是多g5589
性。
g11013g8504,编程时一g14336会先g2031g5326一个具体的对象(Circle,Square,g6122
Triangle),再g6238它g990传到Shape(这g7691g4613g6238对象的具体类型g13485g5548了),
g9994g2530在g1325下来的程序g18336g19766g1363g11004g18039个g2323g2529的Shape reference。
Chapter 10,Detecting Types
www.wgqqh.com/shhgs/tij.html email:shhgs@sohu.com g12544 2 g20041 g1861 17 g20041
g1328为多g5589性的简要g3250g20050,我们再g11004程序来g6563g17860一g17953g990g19766这个g1375g4388,
//,c10:Shapes.java
import com.bruceeckel.simpletest.*;
class Shape {
void draw() { System.out.println(this +
".draw()"); }
}
class Circle extends Shape {
public String toString() { return "Circle"; }
}
class Square extends Shape {
public String toString() { return "Square"; }
}
class Triangle extends Shape {
public String toString() { return "Triangle"; }
}
public class Shapes {
private static Test monitor = new Test();
public static void main(String[] args) {
// Array of Object,not Shape,
Object[] shapeList = {
new Circle(),
new Square(),
new Triangle()
};
for(int i = 0; i < shapeList.length; i++)
((Shape)shapeList[i]).draw(); // Must cast
monitor.expect(new String[] {
"Circle.draw()",
"Square.draw()",
"Triangle.draw()"
});
}
} ///:~
基类g18336有一个g19400g6521g16855g11004toString( )来g6183g2372类的g6183g2372g7643识g12538的draw( )
方法。它g6238this传g13485System.out.println( ),g13792这个方法一看到对象g4613会g14270g2172g16855g11004它的toString( )方法,这g7691g4613g14033g10995g6116对象的Stringg15932
g17810形式了。g8611个g8978g10995类都会g16218写toString( )方法(g1186Objectg13499g6227下来的),这g7691draw( )g4613g14033(多g5589g3332)g6183g2372g2520种对象了。
main( )g2031g5326了一g1135具体的Shape对象并且g6238它们g2164g17839一个g6980组。这个
g6980组有g9869g5630,g3252为它g993是Shape(g4625g12661g2499g1209这g7691),g13792是根类Object的
g6980组。这是在为g1254411章要讲的collection(也g12228container)g1328g19150g3455。
collection是一种g5049具,它只有一种g11004g17896,g4613是要为你g1457g12661g1866它对象。g3252
g8504出g1122g17902g11004性的g13783g15397,这g1135collectiong5224该g14033持有g1231何g1008g16211。所g1209它们持
Thinking in Java 3rd Edition
g12544 3 g20041 g1861 17 g20041 www.wgqqh.com/shhgs/tij.html email:shhgs@sohu.com
有Object。g3252g8504你g2499g1209g1186Objectg6980组看到一个你g4570在g1254411章g17947到的
g18337要问题。
在这个g1375g4388g18336,g5415g6238Shapeg6930g17839Objectg6980组的时g1517,g990传g4613发g10995了。
g11013g1122Javag18336g19766的所有g1008g16211(g19512了primitive)都是对象,g3252g8504Objectg6980
组也g14033持有Shape对象。但是在g990传g17819程中,“这个对象是Shape”
的信息g1014g3845了。对g6980组g13792g16340,它们都只是Object。
但是,g5415你g11004下g7643g6238g1815素g1186g6980组g18336提取出来的时g1517,g4613免g993了要忙活一阵了。g11013g1122g6980组持有的都是g1135Object,g3252g8504提取出来的也g4613g14270g9994都是
Object的reference了。但是我们知道,它实际g990是Shape的
reference,g13792且我们也要向它发送Shape的消息。g1122是老套的
“(Shape)”类型转换g4613变g5483必g993g2499少了。这是RTTI的g7380基本的形式,g3252为程序运行的时g1517会逐项检查转换是g993是都g8503确。这种检查g8503体现了RTTI的本义:在运行时鉴别对象的类型。
这g18336的RTTI转换并g993彻底:Object只是转换g6116Shape,g13792g993是一下
g4388转换g6116Circle,Squareg6122Triangle。这是g3252为现阶段我们只知道
g6980组存储的是Shape。编译的时g1517,这种转换要g11013你g14270己来g6238关,但是运行的时g1517这种转换g4613g993g11004你操心了。
g6521下来g4613交g13485多g5589性了。Shape到底要执行哪段g1207g11733,这要g11013
reference究竟是指向Circle,Square还是Triangle对象来决g4462。
总之程序g4613该这么写;你的g11458g7643g4613是,要让g13489g3835多g6980g1207g11733只同g1207g15932这个类
g13007的基类对象g6183交道(在本案中,g4613是Shape),对它们来说,越少知道下g19766的具体类型越好。这g7691一来,g1207g11733的读写和维护g4613会变g5483更简单,g13792
实现,理解和修改设计方案也会变g5483更容易一g1135。所g1209多g5589性是OOP的一个主要g11458g7643。
但是如g7536你碰到一个特殊问题,g13792要解决这个问题,g7380简单的办法g4613是
“找出这个g17902g11004的reference究竟是指哪个具体类型的”,g18039你g2460该怎么办呢?g8616方说,你要让g11004g6155g14033g11004“g6238这类对象全都染g6116紫色”的办法来突出某种Shape。g6122者,你要写一个会g11004到“旋转”一组shape的方法。但是对圆形来说,旋转是没有意义的,所g1209你只想跳g17819圆形对象。有了RTTI,你g4613g14033问出Shape的reference究竟是指的哪种具体类型的对象了,这g7691你g4613g14033g6238特g1375g13485区别出来了。
Class对象
想要知道Java的RTTI是如何g5049g1328的,你g4613必须首先知道程序运行的时
g1517,类型信息是怎g7691g15932示的。这是g11013一种特殊的,g1457存类的信息的,叫g1328
“Class对象 (Class object)”的对象来完g6116的。实际g990类的“常规”对象是g11013Class对象g2031g5326的。
Chapter 10,Detecting Types
www.wgqqh.com/shhgs/tij.html email:shhgs@sohu.com g12544 4 g20041 g1861 17 g20041
程序g18336的g8611个类都要有一个Class对象。也g4613是说,g8611次你撰写并且编译了一个g7044的类的时g1517,你g4613g2031g5326了一个g7044的Class对象(g13792且g2499g1209这么说,这个对象会存储在同g2529的.class文件g18336)。程序运行时,g5415你需要g2031
g5326一个g18039种类的对象的时g1517,JVM会检查它是否装载了g18039个Class对象。如g7536没有,JVMg4613会去找g18039个.class文件,g9994g2530装载。g11013g8504也g2499知道,Java程序在启g2172的时g1517并没有完全装载,这g9869同许多传统语g16340是g993
一g7691的。
一旦g18039种类型的Class对象被装g17839了内存,所有g18039个类的对象g4613都会g11013
它来g2031g5326了。如g7536这听g990去太玄,g6122者你g993相信的话,下g19766这个程序g4613g14033
g1328证明,
//,c10:SweetShop.java
// Examination of the way the class loader works,
import com.bruceeckel.simpletest.*;
class Candy {
static {
System.out.println("Loading Candy");
}
}
class Gum {
static {
System.out.println("Loading Gum");
}
}
class Cookie {
static {
System.out.println("Loading Cookie");
}
}
public class SweetShop {
private static Test monitor = new Test();
public static void main(String[] args) {
System.out.println("inside main");
new Candy();
System.out.println("After creating Candy");
try {
Class.forName("Gum");
} catch(ClassNotFoundException e) {
System.out.println("Couldn't find Gum");
}
System.out.println("After
Class.forName(\"Gum\")");
new Cookie();
System.out.println("After creating Cookie");
monitor.expect(new String[] {
"inside main",
"Loading Candy",
"After creating Candy",
"Loading Gum",
"After Class.forName(\"Gum\")",
"Loading Cookie",
Thinking in Java 3rd Edition
g12544 5 g20041 g1861 17 g20041 www.wgqqh.com/shhgs/tij.html email:shhgs@sohu.com
"After creating Cookie"
});
}
} ///:~
Candy,Gum,和Cookie,g8611个类都有一条“会在类g12544一次被装载的时g1517执行”的static语句。这g7691装载的类时g1517g4613会它g4613会g6183g2372消息g17902知你了。对象的g2031g5326被分散到了main( )的g2520条g6183g2372语句之g19400,g1866g11458的g4613
是要帮助你检测装载的时机。
你g2499g1209g1186程序的输出看到,Class对象只会在需要的时g1517装载,g13792
static初始化则发g10995在装载类的时g1517。
下g19766这行特别有趣,
Class.forName("Gum");
这是一个Class的static方法(所有的Class对象所共有的)。Class
对象同g1866它对象一g7691,也g2499g1209g11004reference来操控(这是装载器要干的),
g13792要想获取g1866reference,forName( )g4613是一个办法。它要一个g15932示这个类的g2529字的Stringg1328参g6980(一g4462要注意拼写和g3835小写!)。 这个方法会返g3250Class的reference,g993g17819程序g18336g19766没g11004到这个reference;这
g18336只是要g11004它的副g1328g11004g252g252看看Gum类装载了没有,要是还没有g18039g4613g20544
g990装载。装载的g17819程中,程序执行了Gum的static语句。
在g990g17860g1375程中,如g7536Class.forName( )没有找到它要装载的类,g4613会
g6255出一个ClassNotFoundException(太完g13666了!单g1186g5334常的g2529字你
g4613g14033知道出了g1172么问题了)。这g18336,我们只是简单g3332g6265g2590一下问题,g9994g2530
g13499g13505下去,但是在g17751为g3809g7446的程序g18336,你g6122许g5224该在g5334常g3800理程序g18336g6238这个问题解决g6493。
Class常数
Java还提供了一种获取Class对象的reference的方法:“class常g6980
(class literal)”。对g1122g990g17860程序,它g4613是,
Gum.class;
这种写法g993但更简单,g13792且也更g4445全,g3252为它是在编译时g1582检查的。g8504g3818
g11013g1122没有方法g16855g11004,它的执行g6940g10587也更g20652一g1135。
Chapter 10,Detecting Types
www.wgqqh.com/shhgs/tij.html email:shhgs@sohu.com g12544 6 g20041 g1861 17 g20041
Class常g6980g993但g14033g11004g1122g7234g17902类,也g2499g1209g11004g1122g6521g2487,g6980组和primitive类型。g8504g3818,g8611种primitive的wrapper类还有一个g7643g1946的,g2529为TYPE
的g6980g6466g6116g2604。这个TYPEg14033返g3250“g994这种primitive相关g13864的wrapper
类”的Class对象的reference,g4613g1699这g7691,
..,等同于..,
boolean.class Boolean.TYPE
char.class Character.TYPE
byte.class Byte.TYPE
short.class Short.TYPE
int.class Integer.TYPE
long.class Long.TYPE
float.class Float.TYPE
double.class Double.TYPE
void.class Void.TYPE
我g2928g8438g4625g18339g1363g11004“.class”,g3252为这种写法g14033g994g7234g17902类的g1457持一g14280。
转换之前先作检查
到g11458g2081为g8502,你看到的RTTI的形式有,
1,g13475g1868的类型转换;如“(Shape)”,这种转换要g13475g17819RTTI的检查。要是
g1582了g19181g16835的转换,它g4613会g6255出ClassCastException。
2,g1207g15932对象类型的Class对象。你g2499g1209在运行的时g1517查g16822Class对象,g1209
g8504来获取所需的信息。
在C++g18336,g13475g1868的“(Shape)”形式的转换并g993g2172g11004RTTI。它只是简单g3332g2590g16797编译器g6238这个对象g5415g1328g18039个g7044的类型来g11004。但Java会g1328类型检查,所g1209Java的类型转换常常被g12228g1328“类型g4445全的下传”。之所g1209要说
“下传”,是g3252为在g13499g6227关g13007g3282g18336,g8978g10995类一g14336会g6930在下g19766。如g7536g6238
Circle到Shape的转换g12228g1328g990传,g18039么Shape到Circle的转换g4613g5224
该叫下传了。g993g17819你知道Circleg4613是Shape,g13792且编译器也g993会g19471g8502
去g990传,但是你g993一g4462知道Shape是g993是一个 Circle,所g1209如g7536g993g17839
行明确的类型转换的话,编译器是g993会让你g6238对象g17183g13485g8978g10995类的
reference的。
Javag18336g19766还有g12544g989种RTTI的形式。这g4613是instanceof关g19202g16801,它会
g2590g16797你对象是g993是某个类的实g1375。它返g3250的是一个booleang1552,g3252g8504你
g4613g2499g1209g11004提问的形式来g11004了,g4613g1699这g7691,
if(x instanceof Dog)
Thinking in Java 3rd Edition
g12544 7 g20041 g1861 17 g20041 www.wgqqh.com/shhgs/tij.html email:shhgs@sohu.com
((Dog)x).bark();
在g4570x转换g6116Dog之g2081,if语句会先看看x对象是g993是Dog类的。如
g7536没有g1866它信息g14033g2590g16797你这个对象的类型,g18039么在下传之g2081先g11004
instanceof检查一下是很g18337要的;否则的话,你g4613很有g2499g14033会g6770g990
ClassCastException。
g17902常情况下,你只是要找一种类型(g8616如g6238g989g16294形都变g6116紫色的),但是你也g2499g1209g11004instanceofg7643出所有对象的类型。假设你有一个Pet类g13007,
//,c10:Pet.java
package c10;
public class Pet {} ///:~
//,c10:Dog.java
package c10;
public class Dog extends Pet {} ///:~
//,c10:Pug.java
package c10;
public class Pug extends Dog {} ///:~
//,c10:Cat.java
package c10;
public class Cat extends Pet {} ///:~
//,c10:Rodent.java
package c10;
public class Rodent extends Pet {} ///:~
//,c10:Gerbil.java
package c10;
public class Gerbil extends Rodent {} ///:~
//,c10:Hamster.java
package c10;
public class Hamster extends Rodent {} ///:~
Chapter 10,Detecting Types
www.wgqqh.com/shhgs/tij.html email:shhgs@sohu.com g12544 8 g20041 g1861 17 g20041
下g19766,我们要找出g2520种Pet的g6980g18339,所g1209我们要g11004一个g5114int的类来g1457
存这个信息。你g2499g1209g6238它g5415g6116是一个g2499g1209修改的Integer,
//,c10:Counter.java
package c10;
public class Counter {
int i;
public String toString() { return
Integer.toString(i); }
} ///:~
下一g8505,我们需要一个g14033同时持有两种对象的g5049具:一个g15932示Pet的类型,另一个是g15932示g4468g10301g6980g18339的Counter。也g4613是说,我们要问“这g18336有多少Gerbil对象?”g7234g17902g6980组g1328g993了这个,g3252为它是g11004下g7643来g4462g1313的。
我们要的是g14033g11004Pet的类型来g4462g1313的g6980组。我们要g4570Counter对象同
Pet对象关g13864起来。为了g14033g1946确的g1582到这一g9869,我们会g11004到一种被g12228为
“关g13864性g6980组(associative array)”的g7643g1946g6980g6466结构。下g19766是它g7380简单的实现,
//,c10:AssociativeArray.java
// Associates keys with values,
package c10;
import com.bruceeckel.simpletest.*;
public class AssociativeArray {
private static Test monitor = new Test();
private Object[][] pairs;
private int index;
public AssociativeArray(int length) {
pairs = new Object[length][2];
}
public void put(Object key,Object value) {
if(index >= pairs.length)
throw new ArrayIndexOutOfBoundsException();
pairs[index++] = new Object[] { key,value };
}
public Object get(Object key) {
for(int i = 0; i < index; i++)
if(key.equals(pairs[i][0]))
return pairs[i][1];
throw new RuntimeException("Failed to find key");
}
public String toString() {
String result = "";
for(int i = 0; i < index; i++) {
result += pairs[i][0] + "," + pairs[i][1];
if(i < index - 1) result += "\n";
}
return result;
}
Thinking in Java 3rd Edition
g12544 9 g20041 g1861 17 g20041 www.wgqqh.com/shhgs/tij.html email:shhgs@sohu.com
public static void main(String[] args) {
AssociativeArray map = new AssociativeArray(6);
map.put("sky","blue");
map.put("grass","green");
map.put("ocean","dancing");
map.put("tree","tall");
map.put("earth","brown");
map.put("sun","warm");
try {
map.put("extra","object"); // Past the end
} catch(ArrayIndexOutOfBoundsException e) {
System.out.println("Too many objects!");
}
System.out.println(map);
System.out.println(map.get("ocean"));
monitor.expect(new String[] {
"Too many objects!",
"sky,blue",
"grass,green",
"ocean,dancing",
"tree,tall",
"earth,brown",
"sun,warm",
"dancing"
});
}
} ///:~
如g7536是g12544一次看到这个程序,你会g16285g5483这好g1699是一个g17902g11004g5049具,g5224该g6238它
g6930g17839com.bruceeckel.tools。是的,这的确是一个g17902g11004g5049具g252g252太有
g11004了,g1209g14279g1122java.util提供了好g1972种关g13864性g6980组(g1866g8503式的g2641g2529是
Map),它们的g2163g14033g8616这g2499强多了,g17907g5242也g5567了许多。g1254411章会g11004g3835g18339
的g12699g5145讲解关g13864性g6980组。但是它们太g3809g7446了,所g1209这g18336g1032时g1328了一个,g11458
的g4613是要让你在理解关g13864性g6980组的g1227g1552的同时,g993g14279g1122让问题变g5483太g3809
g7446。
在关g13864性g6980组中,下g7643被g12228为g19202(key),g13792g994之相关g13864的对象则被g12228为g1552
(value) 。这g18336,我们g11004“构g5326一个g2464g1815素g6980组的pairg6980组,并且g11004它来g1457存g19202和g1552”的方法,g5326g12447了g19202g1552之g19400的关g13864。构g17908g2001g6980g2031g5326的是一个
g4462g19283g6980组,g3252g8504你g5483g11004index来确g1457它g993会g17819g11040。put( )一对g7044的g19202g1552
时,它会g2031g5326一个g7044的g2464g1815素g6980组,并且g6238它g6930到pairs的下一个g12366g1313
g18336。如g7536indexg3835g1122g12573g1122pairs的g19283g5242,它g4613g6255出g5334常。
要g11004get( )的时g1517,你只要g6238keyg5415参g6980传g13485它g4613g2499g1209了,它会帮你找出g1552,g9994g2530g6238结g7536返g3250g13485你,要是找g993到,它g4613g6255g5334常。get( )g11004了一种你g14033想到的g7380g5942的g12651法:g1186g6980组的g5332g3848,g11004equals( )g1328g8616g17751。但是这g18336更看g18337简单g13792g993是g6940g10587,g13792且g1254411章要g4570的Mapg5062g13475解决了这个问题,g3252g8504我们g993必g6297心。
Chapter 10,Detecting Types
www.wgqqh.com/shhgs/tij.html email:shhgs@sohu.com g12544 10 g20041 g1861 17 g20041
put( )和get( )是关g13864性g6980组的两个基本方法,但是g13783g15397到程序的输出,我们还g16218写了toString( )方法,让它g6238g19202和g1552全都g6183g2372出来。为了看看它是g993是g14033g8503常g5049g1328,main( )先是g5460AssociativeArrayg18336g3647
了一组g6116对的字g12538g1030,g9994g2530g6238它g6183g2372了出来,g7380有再g11004get( )提取了一个g1552。
现在所有g5049具都g5062g13475g1946g3803好了,我们g2499g1209g11004instanceof来g6980Pet了,
//,c10:PetCount.java
// Using instanceof,
package c10;
import com.bruceeckel.simpletest.*;
import java.util.*;
public class PetCount {
private static Test monitor = new Test();
private static Random rand = new Random();
static String[] typenames = {
"Pet","Dog","Pug","Cat",
"Rodent","Gerbil","Hamster",
};
// Exceptions thrown to console,
public static void main(String[] args) {
Object[] pets = new Object[15];
try {
Class[] petTypes = {
Class.forName("c10.Dog"),
Class.forName("c10.Pug"),
Class.forName("c10.Cat"),
Class.forName("c10.Rodent"),
Class.forName("c10.Gerbil"),
Class.forName("c10.Hamster"),
};
for(int i = 0; i < pets.length; i++)
pets[i] =
petTypes[rand.nextInt(petTypes.length)]
,newInstance();
} catch(InstantiationException e) {
System.out.println("Cannot instantiate");
System.exit(1);
} catch(IllegalAccessException e) {
System.out.println("Cannot access");
System.exit(1);
} catch(ClassNotFoundException e) {
System.out.println("Cannot find class");
System.exit(1);
}
AssociativeArray map =
new AssociativeArray(typenames.length);
for(int i = 0; i < typenames.length; i++)
map.put(typenames[i],new Counter());
for(int i = 0; i < pets.length; i++) {
Object o = pets[i];
if(o instanceof Pet)
((Counter)map.get("Pet")).i++;
if(o instanceof Dog)
((Counter)map.get("Dog")).i++;
if(o instanceof Pug)
Thinking in Java 3rd Edition
g12544 11 g20041 g1861 17 g20041 www.wgqqh.com/shhgs/tij.html email:shhgs@sohu.com
((Counter)map.get("Pug")).i++;
if(o instanceof Cat)
((Counter)map.get("Cat")).i++;
if(o instanceof Rodent)
((Counter)map.get("Rodent")).i++;
if(o instanceof Gerbil)
((Counter)map.get("Gerbil")).i++;
if(o instanceof Hamster)
((Counter)map.get("Hamster")).i++;
}
// List each individual pet,
for(int i = 0; i < pets.length; i++)
System.out.println(pets[i].getClass());
// Show the counts,
System.out.println(map);
monitor.expect(new Object[] {
new TestExpression("%% class c10\\."+
"(Dog|Pug|Cat|Rodent|Gerbil|Hamster)",
pets.length),
new TestExpression(
"%% (Pet|Dog|Pug|Cat|Rodent|Gerbil|Hamster)"
+
",\\d+",typenames.length)
});
}
} ///:~
main( )g11004Class.forName( )g2031g5326了一个g2529为petTypes的Class
对象的g6980组。g11013g1122Pet对象g4658g1122c10这个package,g3252g8504g2641g2529的时g1517
要g6238package的g2529字也g2265g6336g17839去。
g6521下来g4613要g6238petsg6980组g3647g9397了。这g18336g11004的方法是,先在petTypesg6980
组g18336g19766g19555机g17885取一个Class对象,再g16855g11004它的Class.newInstance( )
方法,g13792这个方法会g16855g11004类的g21676g16760(也g4613是g7092参g6980的)构g17908g2001g6980,这g7691g4613g14033
g10995g6116一个g7044的对象了。
forName( )和newInstance( )都会g1147g10995g5334常,g6122许你g5062g13475g1186try
g2462g1866g2530g19766的catch语句看出了这一g9869。g8504g3818,g5334常g2529再一次向我们解g18334
了出了g1172么g19181。(IllegalAccessExceptiong994g17841g2465Java的g4445全机制有关)。
g2031g5326完AssociativeArray之g2530,我们再g11004Pet和Counter的g19202g1552
组g2524g6238它g3647g9397。g6521下来g4613要g11004instanceof来测g16809g6980组中的g2520个g1815素,
并且为它们计g6980了。g7380g2530,我们还要g6183g2372petsg6980组和
AssociativeArray,这g7691你g4613g14033g8616g17751结g7536了。
instanceof的g19492制很g1017:你只g14033g6355它同类的g2529字,g13792g993是Class对象
g1328g8616g17751。g2499g14033你g16285g5483要写g18039么多instanceof实在是太g7092g13854了,确实如
g8504。但是“先g2031g5326一个Class对象的g6980组,再g6355它来g1328g8616g17751”的方法,
看g990去是g6414g13886明的,实际g990g2376行g993g17902(别g17220g5332,你g20544g990g4613g14033看到g7379g1207方案
Chapter 10,Detecting Types
www.wgqqh.com/shhgs/tij.html email:shhgs@sohu.com g12544 12 g20041 g1861 17 g20041
了)。但是这个g19492制也没有你想的g18039么g1017g18337,g3252为g5415你发现instanceof
太多的时g1517,你g4613知道设计方案有问题了。
g5415g9994这个g1375g4388有g1135g1582g1328g252g252你完全g2499g1209在类g18336g6930一个staticg6116g2604,g9994g2530
g11004构g17908g2001g6980去g17894g3698它,这g7691g4613g14033g6496控对象的g6980g11458了。要是你g14033看到这个类的g9316g1207g11733的话,你g4613g2499g1209g11004这个方法来g1328修改了。g993g17819情况g993总是这g7691,
所g1209我们需要RTTI。
使用类常数
g14033看看怎g7691g11004类常g6980来g18337写PetCount.java也是很有意g5617的。g13792且g18337
写之g2530,程序看g990去更g9177g7982了。
//,c10:PetCount2.java
// Using class literals,
package c10;
import com.bruceeckel.simpletest.*;
import java.util.*;
public class PetCount2 {
private static Test monitor = new Test();
private static Random rand = new Random();
public static void main(String[] args) {
Object[] pets = new Object[15];
Class[] petTypes = {
// Class literals,
Pet.class,
Dog.class,
Pug.class,
Cat.class,
Rodent.class,
Gerbil.class,
Hamster.class,
};
try {
for(int i = 0; i < pets.length; i++) {
// Offset by one to eliminate Pet.class,
int rnd = 1 + rand.nextInt(petTypes.length -
1);
pets[i] = petTypes[rnd].newInstance();
}
} catch(InstantiationException e) {
System.out.println("Cannot instantiate");
System.exit(1);
} catch(IllegalAccessException e) {
System.out.println("Cannot access");
System.exit(1);
}
AssociativeArray map =
new AssociativeArray(petTypes.length);
for(int i = 0; i < petTypes.length; i++)
map.put(petTypes[i].toString(),new Counter());
for(int i = 0; i < pets.length; i++) {
Object o = pets[i];
if(o instanceof Pet)
((Counter)map.get("class c10.Pet")).i++;
Thinking in Java 3rd Edition
g12544 13 g20041 g1861 17 g20041 www.wgqqh.com/shhgs/tij.html email:shhgs@sohu.com
if(o instanceof Dog)
((Counter)map.get("class c10.Dog")).i++;
if(o instanceof Pug)
((Counter)map.get("class c10.Pug")).i++;
if(o instanceof Cat)
((Counter)map.get("class c10.Cat")).i++;
if(o instanceof Rodent)
((Counter)map.get("class c10.Rodent")).i++;
if(o instanceof Gerbil)
((Counter)map.get("class c10.Gerbil")).i++;
if(o instanceof Hamster)
((Counter)map.get("class c10.Hamster")).i++;
}
// List each individual pet,
for(int i = 0; i < pets.length; i++)
System.out.println(pets[i].getClass());
// Show the counts,
System.out.println(map);
monitor.expect(new Object[] {
new TestExpression("%% class c10\\." +
"(Dog|Pug|Cat|Rodent|Gerbil|Hamster)",
pets.length),
new TestExpression("%% class c10\\." +
"(Pet|Dog|Pug|Cat|Rodent|Gerbil|Hamster),
\\d+",
petTypes.length)
});
}
} ///:~
这g18336我们想g16809g16809g11464g6521g1186Class对象g18039g18336g5483到类的g2529字,g3252g8504g6238
typenamesg6980组去g6493了。注意,g13007统g2499g1209分g17788哪g1135是类,哪g1135是g6521
g2487。
g8504g3818,g2031g5326petTypes的时g1517g5062g13475g993需要g11004try了。编译的时g1517g5062g13475g1582
g17819检查了,g3252g8504它g993会g1699Class.forName( )g18039g7691g6255出g5334常的。
再有,g2172g5589g2031g5326Pet对象的时g1517,g10995g6116的g19555机g6980是有g19492制的,它g5224该小
g1122petTypes.lengthg13792且g993g2265g6336g19658。这是g3252为g19658g1207g15932了Pet.class,
g13792我们对g8879型的Pet对象g993g5875g1864趣。但是,g11013g1122petTypes也g2265g6336
Pet.class,所g1209计g6980的时g1517也g6238Petg12651了一g17953。
动态的instanceof
Class.isInstance方法还提供了一种g2172g5589g16855g11004instanceof的方法。
这g7691,PetCountg18336g19766g18039g1135g997g19487的instanceofg4613都g14033被去g19512了。
//,c10:PetCount3.java
// Using isInstance()
package c10;
import com.bruceeckel.simpletest.*;
import java.util.*;
Chapter 10,Detecting Types
www.wgqqh.com/shhgs/tij.html email:shhgs@sohu.com g12544 14 g20041 g1861 17 g20041
public class PetCount3 {
private static Test monitor = new Test();
private static Random rand = new Random();
public static void main(String[] args) {
Object[] pets = new Object[15];
Class[] petTypes = {
// Class literals,
Pet.class,
Dog.class,
Pug.class,
Cat.class,
Rodent.class,
Gerbil.class,
Hamster.class,
};
try {
for(int i = 0; i < pets.length; i++) {
// Offset by one to eliminate Pet.class,
int rnd = 1 + rand.nextInt(petTypes.length -
1);
pets[i] = petTypes[rnd].newInstance();
}
} catch(InstantiationException e) {
System.out.println("Cannot instantiate");
System.exit(1);
} catch(IllegalAccessException e) {
System.out.println("Cannot access");
System.exit(1);
}
AssociativeArray map =
new AssociativeArray(petTypes.length);
for(int i = 0; i < petTypes.length; i++)
map.put(petTypes[i].toString(),new Counter());
for(int i = 0; i < pets.length; i++) {
Object o = pets[i];
// Using Class.isInstance() to eliminate
// individual instanceof expressions,
for(int j = 0; j < petTypes.length; ++j)
if(petTypes[j].isInstance(o))
((Counter)map.get(petTypes[j].toString())).i++;
}
// List each individual pet,
for(int i = 0; i < pets.length; i++)
System.out.println(pets[i].getClass());
// Show the counts,
System.out.println(map);
monitor.expect(new Object[] {
new TestExpression("%% class c10\\." +
"(Dog|Pug|Cat|Rodent|Gerbil|Hamster)",
pets.length),
new TestExpression("%% class c10\\." +
"(Pet|Dog|Pug|Cat|Rodent|Gerbil|Hamster),
\\d+",
petTypes.length)
});
}
} ///:~
Thinking in Java 3rd Edition
g12544 15 g20041 g1861 17 g20041 www.wgqqh.com/shhgs/tij.html email:shhgs@sohu.com
g2499g1209这么说,isInstance( )g14033完全g7379g1207instanceof。g8504g3818,它还g14033
让你g11004修改petTypesg6980组的方式g9167g2164g7044的Pet类型;程序的g1866g1325g18108分
g7092需改g2172(g13792g11004instanceof的话,g4613g993g5483g993g1328修改了)。
instanceof vs,Class的相等性
有两种方法g2499g1209查出对象的类型信息,一是instanceof(g2265g6336
instanceof和isInstance( ),它们g6940g7536是相同的),另一个g4613是g8616g17751
Class对象,这两者之g19400有g11540g18337g3835的区别。下g19766这段程序g4613向你g9448示了它们之g19400的区别,
//,c10:FamilyVsExactType.java
// The difference between instanceof and class
package c10;
import com.bruceeckel.simpletest.*;
class Base {}
class Derived extends Base {}
public class FamilyVsExactType {
private static Test monitor = new Test();
static void test(Object x) {
System.out.println("Testing x of type " +
x.getClass());
System.out.println("x instanceof Base " +
(x instanceof Base));
System.out.println("x instanceof Derived " +
(x instanceof Derived));
System.out.println("Base.isInstance(x) " +
Base.class.isInstance(x));
System.out.println("Derived.isInstance(x) " +
Derived.class.isInstance(x));
System.out.println("x.getClass() == Base.class "
+
(x.getClass() == Base.class));
System.out.println("x.getClass() ==
Derived.class " +
(x.getClass() == Derived.class));
System.out.println("x.getClass().equals(Base.class))
"+
(x.getClass().equals(Base.class)));
System.out.println(
"x.getClass().equals(Derived.class)) " +
(x.getClass().equals(Derived.class)));
}
public static void main(String[] args) {
test(new Base());
test(new Derived());
monitor.expect(new String[] {
"Testing x of type class c10.Base",
"x instanceof Base true",
"x instanceof Derived false",
"Base.isInstance(x) true",
Chapter 10,Detecting Types
www.wgqqh.com/shhgs/tij.html email:shhgs@sohu.com g12544 16 g20041 g1861 17 g20041
"Derived.isInstance(x) false",
"x.getClass() == Base.class true",
"x.getClass() == Derived.class false",
"x.getClass().equals(Base.class)) true",
"x.getClass().equals(Derived.class)) false",
"Testing x of type class c10.Derived",
"x instanceof Base true",
"x instanceof Derived true",
"Base.isInstance(x) true",
"Derived.isInstance(x) true",
"x.getClass() == Base.class false",
"x.getClass() == Derived.class true",
"x.getClass().equals(Base.class)) false",
"x.getClass().equals(Derived.class)) true"
});
}
} ///:~
test( )方法g11004了两种形式的instanceof来检查参g6980的类型。g6521g11540它获取了Class对象的reference,g9994g2530g11004==和equals( )测g16809g1866相g12573
性。g1552g5483g8439g5956的是,instanceof同isInstance( )g5483出的结g7536是相同的,equals( )和==也一g7691。但是这两组这两组测g16809所g5483出的结g16782g2376是
g993同的。instanceofg17993g5502了类型的g8022g5577,它问的是“你是这个类,g6122者是g1186这个类g8978g10995出的类吗?”g13792g11004 == 去g8616g17751两个Class对象g4613g994g13499g6227
g7092关了g252g252它要么是这个具体类型的,要么g4613g993是。
RTTI的语法
Java要g11004Class对象来g17839行RTTI,甚g14279类型转换也要g11004到它。g8504g3818,
Class类还提供了很多g1866它g994RTTI相关的g11004g17896。
首先,你必须g5483到所需的Class对象的reference。一种办法是在g2081g19766
g1375程中g9448示的g4570一个字g12538g1030传g13485Class.forName( )方法。这个方法很方g1427,g3252为这g7691一来,你g4613g993再需要为获取某个类的Class对象的
referenceg13792去事先g1946g3803一个g18039个类的对象了。但是如g7536你g5062g13475有一个g18039
种类的对象的话,你g4613g14033g11004getClass( )来获取Class的reference
了。这是Object根类的方法,它会返g3250对象所g4658的真实类型的Class
对象的reference。下g19766这段程序g9448示了很多有趣的Class类的方法。
//,c10:ToyTest.java
// Testing class Class,
import com.bruceeckel.simpletest.*;
interface HasBatteries {}
interface Waterproof {}
interface Shoots {}
class Toy {
// Comment out the following default constructor
// to see NoSuchMethodError from (*1*)
Toy() {}
Thinking in Java 3rd Edition
g12544 17 g20041 g1861 17 g20041 www.wgqqh.com/shhgs/tij.html email:shhgs@sohu.com
Toy(int i) {}
}
class FancyToy extends Toy
implements HasBatteries,Waterproof,Shoots {
FancyToy() { super(1); }
}
public class ToyTest {
private static Test monitor = new Test();
static void printInfo(Class cc) {
System.out.println("Class name," + cc.getName()
+
" is interface? [" + cc.isInterface() + "]");
}
public static void main(String[] args) {
Class c = null;
try {
c = Class.forName("FancyToy");
} catch(ClassNotFoundException e) {
System.out.println("Can't find FancyToy");
System.exit(1);
}
printInfo(c);
Class[] faces = c.getInterfaces();
for(int i = 0; i < faces.length; i++)
printInfo(faces[i]);
Class cy = c.getSuperclass();
Object o = null;
try {
// Requires default constructor,
o = cy.newInstance(); // (*1*)
} catch(InstantiationException e) {
System.out.println("Cannot instantiate");
System.exit(1);
} catch(IllegalAccessException e) {
System.out.println("Cannot access");
System.exit(1);
}
printInfo(o.getClass());
monitor.expect(new String[] {
"Class name,FancyToy is interface? [false]",
"Class name,HasBatteries is interface?
[true]",
"Class name,Waterproof is interface? [true]",
"Class name,Shoots is interface? [true]",
"Class name,Toy is interface? [false]"
});
}
} ///:~
FancyToy类相g5415g3809g7446,它g13499g6227了Toy,实现了HasBatteries,
Waterproof和Shootsg6521g2487。main( )g18336边先g2031g5326一个Class的
reference,g9994g2530g11004“g11013tryg6336起来的”forName( )方法g6238它初始化为
FancyToy的Class对象。
Chapter 10,Detecting Types
www.wgqqh.com/shhgs/tij.html email:shhgs@sohu.com g12544 18 g20041 g1861 18 g20041
Class.getInterfaces( )方法会返g3250一个Class对象的g6980组。g6980组中的对象分别g15932示了它所实现的g6521g2487。
如g7536你手g990有一个Class对象,你还g14033g11004getSuperclass( )问出它g7380
近的g18039个父类。g5415g9994,这会返g3250一个Class的reference,g1122是你g2499g1209
g6521g11540问。也g4613是说,程序运行的时g1517,你g14033g1209g8504发现对象的完整的类g13007。
初看起来,Class的newInstance( )方法g4613g1699是另一种clone( )对象的方法。但是,你g2376g2499g1209g11004newInstance( )凭空g2031g5326出一个g7044的对象。g4613g1699这g18336所看到的,程序g18336g19766没有Toy对象g252g252只有一个指向
“Toy类的Class对象”的reference,cy。这是一种实现“虚拟构g17908
g2001g6980(virtual constructor)”的方法,它g14033让你写出“我g993g12661你到底是g1172
么类的,但是你一g4462要g6238你g14270己g2031g5326好”的g1207g11733。在g990g17860程序中,cy只是一个Class对象的reference,g3252g8504编译器g5483到的信息非常有g19492。所
g1209g5415方法返g3250对象的时g1517,你只g14033g6238它交g13485Object的reference。但是这个reference指的g2376是一个Toy的对象。g5415g9994,在你g14033向它发送“g19512
Objectg6521g2487g1209g3818”的消息之g2081,还g5483g16855查一番,并g1582一个类型转换。g8504
g3818,g11004newInstance( )g2031g5326的对象的g2081提是,这个类还必须要有g21676g16760
构g17908g2001g6980。下一节,我们会讲Java的reflection API,届时你g4613g14033g11004g1231
意一个构g17908g2001g6980来g2172g5589的g2031g5326对象了。
这份g9177单的g7380g2530一项g4613是printInfo( )方法。它g6355一个Class对象的
referenceg1328参g6980,g11004getName( )提取类的g2529字,g11004isInterface( )
判断它是g993是g6521g2487。这g7691,你g4613g14033仅凭Class对象g4613找出所有你想知道的这个对象的信息了。
Reflection,运行时的类信息
如g7536你g993知道对象的确切类型,RTTI会g2590g16797你。但是它有一个g2081提:为了让RTTIg14033找出这g1135类型,并且利g11004这g1135信息,这g1135类必须是编译时g5062
知的。换g16340之,要想让RTTIg8503常运行,编译器必须知道所有的类。
初看起来,这也g993g12651是g1172么g19492制,但是如g7536你g5483到一个g993在程序g12661辖范围之内的对象的reference,g18039g2460该怎么办呢。实际g990,程序编译的时g1517,
根本g5483g993到这个对象的class文件。g8616方说,你g6355到一张磁盘,g6122者g6183g5332
了一个网络连g6521,g9994g2530你被g2590知这g1135g6980g6466g1207g15932某个类。但是编译程序的时
g1517,编译器g993g2499g14033知道g4570来出现的类,g18039么你该怎g7691g1363g11004这个类呢?
在传统的编程环境中,这种假设有g9869牵强。但是g5415我们转向更g3835的编程世
g11040的时g1517,g4613会发现这种事情会出现在一g1135很g18337要的g3332方。首先要碰到的
g4613是“基g1122组件的编程(component-based programming)”,也g4613是
g11004Rapid Application Development(RAD)g5049具搞g5332发时所g11004的技术。
这是一种,“g17902g17819g4570g1207g15932组件的g3282g7643拖到窗体来g2031g5326程序的”g2499视化编程方式(在这种g5332发方式下,你在屏幕g990看到的g4613是“窗体”)。g9994g2530,编程
Thinking in Java 3rd Edition
g12544 19 g20041 g1861 19 g20041 www.wgqqh.com/shhgs/tij.html email:shhgs@sohu.com
的时g1517g4613要设置组件的g1552,并g1209g8504来g17839行配置。设计阶段的配置要求,所有组件都g14033被实g1375化,g13792且要g14033公g5332g1866g18108分细节,并允许你设置和读取它的g1552。另g3818,g3800理GUI事件的组件还必须公g5332g1866某g1135方法的细节,这g7691
程序g2604才g14033借助RADg5049具去g16218写g18039g1135g3800理事件的方法。Reflection提供了这g7691一种机制,它g14033侦测出组件到底有哪g1135方法,g9994g2530返g3250这g1135方法的
g2529字。Javag1209JavaBeans的形式提供了基g1122组件的编程的支持。
还有一种“会让你迫切希望g14033在程序运行时找出类的信息”的情形,这g4613
是g17902g17819网络在远程机器g990g2031g5326对象并运行程序。这被g12228为“远程方法g16855g11004
(Remote Method Invocation缩写是RMI)”。它g14033让一个Java程序
g4570对象分布到很多机器g990。这种分布g2499g1209有很多原g3252。g8616如,你g8503在g17839行一项计g12651密集的g1231务,为了g2164g5567计g12651g17907g5242,你要g6238g1231务分拆g5332来,g6930到g12366
闲的机器g990。g6122者,你要g6238g3800理某g1135特殊g1231务的g1207g11733(g8616如在一个多层的
g4470g6155/服务器架构中,g6238“业务规则”)g6930到一台特g4462的机器g990,g1122是这台机器g4613g6116了g6563g17860这g1135行为的共g11004信息库了,这g7691g13007统的g1231何一个修改g4613都
g14033很轻易的发g10995在g8611个人的身g990了。(这是一种很有趣的设计,g3252为机器只是为了简化软件修改g13792设的!)g19512了这g1972条,分布式计g12651还g14033g11004来发挥一g1135专g11004机的特g19283,有g1135机器g8616g17751擅g19283一g1135特殊运g12651g252g252g8616如矩阵倒置g252
g252g13792g11004g1122g17902g11004g1231务的时g1517g4613显g5483g993太g2524适g6122太g17819昂贵了。
g19512了Class类(本章g2081g19766g5062g13475讲g17819了),还有一个类库,
java.lang.reflect也支持reflection。这个类库g18336g19766有Field,
Method,和Constructor类(它们都实现了Memberg6521g2487)。运行时,JVM会g2031g5326一g1135这种类的对象来g1207g15932未知类的g2520个g6116g2604。g9994g2530,你
g4613g14033g11004Constructor来g2031g5326g7044的对象,g11004get( )和set( )来读取和修改g994Field对象相关g13864的g6116g2604g6980g6466,g11004invoke( )方法g16855g11004g994Method
对象相关g13864的方法了。g8504g3818,你还g14033g11004getFields( ),
getMethods( ),getConstructors( )之类的方法,获取g15932示g6116g2604
g6980g6466,方法g6122构g17908g2001g6980的对象g6980组。(请查阅JDK文档g1209获取更多关g1122
Class类的细节)g11013g8504,g2375g1427编译时g1172么信息都g5483g993到,你也有办法g14033在运行时问出g2323g2529对象的全g18108类型信息了。
有一g9869很g18337要,reflectiong993是g1172么妖术。g5415你g11004reflectiong994未知类的对象g6183交道的时g1517,JVM(会和g7234g17902的RTTI一g7691)先看看这个对象是g4658g1122
g18039个具体类型的,但是g8504g2530,它还是g5483先装载Class对象才g14033g5049g1328。也
g4613是,g993g12661是g1186本g3332还是g1186网络,g2465g8503JVM必须g6355到g18039个.class文件。
所g1209RTTI同reflection的真g8503区别在g1122,RTTI是在编译时让编译器g6183
g5332并且检查.class文件。换句话说,你是在g17902g17819“g8503常”g17896径g16855g11004对象的方法。g13792对reflection来说,编译时是g5483g993到.class文件的;所g1209它是在运行时g6183g5332并检查g18039个文件。
一个提取类的方法的程序
Chapter 10,Detecting Types
www.wgqqh.com/shhgs/tij.html email:shhgs@sohu.com g12544 20 g20041 g1861 20 g20041
一g14336来说,你g993太会g11464g6521g1363g11004reflection;Java之所g1209要有这种g2163g14033,
是要g11004它来支持一g1135别的特性,g8616如对象的序列化(g1254412章)和
JavaBeans(g1254414章)。g993g17819在有g1135情况下,g14033g2172g5589提取类的信息还是很有g11004的。如g7536有个g14033提取类的方法的g5049具,g18039g4613太好了。g8503如我们g2081g19766所讲的,读g9316g1207g11733g6122是查JDK文档的时g1517,你只g14033找到这个类的g4462义g18336g19766
有的g6122是g16218写的方法。但是这个类还g2499g14033g1186g1866基类g18039g18336g13499g6227了很多别的方法。要g6238它们全都找出来会非常吃力。1所幸的是,我们g2499g1209g11004
reflection写一个小程序,让它g14270g2172显示类的完整g6521g2487。下g19766g4613是它g5049g1328
的方式,
//,c10:ShowMethods.java
// Using reflection to show all the methods of a
class,
// even if the methods are defined in the base class,
// {Args,ShowMethods}
import java.lang.reflect.*;
import java.util.regex.*;
public class ShowMethods {
private static final String usage =
"usage,\n" +
"ShowMethods qualified.class.name\n" +
"To show all methods in class or,\n" +
"ShowMethods qualified.class.name word\n" +
"To search for methods involving 'word'";
private static Pattern p =
Pattern.compile("\\w+\\.");
public static void main(String[] args) {
if(args.length < 1) {
System.out.println(usage);
System.exit(0);
}
int lines = 0;
try {
Class c = Class.forName(args[0]);
Method[] m = c.getMethods();
Constructor[] ctor = c.getConstructors();
if(args.length == 1) {
for(int i = 0; i < m.length; i++)
System.out.println(
p.matcher(m[i].toString()).replaceAll(""));
for(int i = 0; i < ctor.length; i++)
System.out.println(
p.matcher(ctor[i].toString()).replaceAll(""));
lines = m.length + ctor.length;
} else {
for(int i = 0; i < m.length; i++)
if(m[i].toString().indexOf(args[1]) != -1)
{
System.out.println(
p.matcher(m[i].toString()).replaceAll(""));
1g10317g2047g7171g17819g2447g452g993g17819Sung5062g13475g1328g1114g5468g3835g11352g6925g17839g712g6164g1209g10628g3324g7609g3534g12879g5062g13475g12628g2345g1114g5468g3822g452
Thinking in Java 3rd Edition
g12544 21 g20041 g1861 21 g20041 www.wgqqh.com/shhgs/tij.html email:shhgs@sohu.com
lines++;
}
for(int i = 0; i < ctor.length; i++)
if(ctor[i].toString().indexOf(args[1]) !=
-1) {
System.out.println(p.matcher(
ctor[i].toString()).replaceAll(""));
lines++;
}
}
} catch(ClassNotFoundException e) {
System.out.println("No such class," + e);
}
}
} ///:~
Class的getMethods( )和getConstructors( )方法分别会返回一个Method和一个Constructor数组。这两个类又包括一些“能把它们所代表的方法的名字,参数,返回值全部拆解开来”的方法。不过你也可以像这里所做的,只用toString( )去获取一个包括这个方法的全部特征签名的String。剩下的代码就是用来抽取命令行信息,以及判断方法特征是否与你你输入的字符串相匹配(用indexOf( )),并且把匹配的方法列出来的。
为了能把“java.lang”这样的限定词从“java.lang.String”当中抽取出来,我们用了JDK 1.4所提供的正则表达式(regular
expression)。这是一种强大而简洁的工具,有些语言已经用了很长时间了。你已经从com.bruceeckel.simpletest.Test的expect( )语句中体会到正则表达式的简洁了,而上面那个程序又向你g9448g12046了正则表达式的g3534g7424g13546程g8505g20600。
import java.util.regexg1055g2530,你g20330g1820用static的
Pattern.compile()方法g13546g16805正则表达式。它会返回一个用这个字符串
g10995g6116的Patterng4557g16949。这里,这个参数就是
"\\w+\\."
g16213g5831g5336g6038这个正则表达式,以及g1866它正则表达式g18129表g12046g1172g1052g5859g5617,g16843g7609g19417
JDKg7003g7735的java.util.regex.Pattern部分。g4557g1122这个表达式,你g16213
g11705g17959‘\w’表g12046“一个g2345词字符g726[a-zA-Z_0-9]”。 g254+g255的g5859g5617是
“一个g6122g3822个g2081面的表达式”g252g252所以它表g12046一个g6122g3822个g2345词字符g252g252以及g1866g2530的g9869,g254\.g255(不能只是一个g9869,g3324正则表达式里,g2345g10432一个g9869g2507
表g12046“g1231g5859字符”)。所以这个表达式会匹配“一个g11013g2345个g6122g3822个g2345词字符g1889g2164上一个g9869g2507g7512g6116”的字符序列,而这正是我们g16213g2105g19512的。
Chapter 10,Detecting Types
www.wgqqh.com/shhgs/tij.html email:shhgs@sohu.com g12544 22 g20041 g1861 22 g20041
经过g13546g16805获取了Patterng4557g16949g1055g2530,你就可以把g5465g3800g10714的字符串g1268g13485它的matcher( )方法了。这个方法会返回一个Matcherg4557g16949,这个g4557
g16949包括了一g3883可供你g17885g6333的g6817g1328(g5326g16770你去g11487g11487JDKg7003g7735的
java.util.regex.Matcher部分)。这里只用到了它的replaceAll( )
方法,它会把字符串里所有与表达式相匹配的部分全部g7379g6454g6116g12366字符g252g252
也就是把它们全g2036了。
g17836有一种g7368简g2345的方法,你可以g11464g6521g2045用Stringg1881g13634的正则表达式。g8616
g3926,上面那个程序的replaceAll( )g726
p.matcher(ctor[i].toString()).replaceAll("")
可以g6925g1901g6116
ctor[i].toString().replaceAll("\\w+\\.","")
这样就不用g20056g1820g13546g16805正则表达式了。这种做法很g17878g2524那些只用一g8437的正则表达式,g1306是g3926g7536g16213g2465g3809g1363用正则表达式,g20056g13546g16805的方式会g8616g17751g20652g6940。而
g7424g1375g4658g1122g2530g13785。
这g8585程序g9448g12046了reflection是g5602样工g1328的,g11013g1122Class.forName( )所返回的g13479g7536g3324g13546g16805时是g7092法g20056g11705的,g3252g8504这些方法的特征签名g5224g16825是g3324g17828
行时获取的。g3926g7536你g11752g12362过JDK的g7003g7735的g16817,你就会g11705g17959reflection的
g2163能g1055强,g17287以g16765你g3324g17828行时g2031g5326一个“你g3324g13546g16805时一g7092所g11705的类”的g4557
g16949,并g16855用它的方法(g7424g1082的g2530面会有这个g1375g4388的)。g4625g12661开g3999的时g1517,你会g16760为你大g8022g8716g17840也不用不到这些g2163能,g1306是reflection的g1227值g1055g20652会出
g1058你的g5859g7021。
提g12046一下这个程序g16825g5602样g17828行
java ShowMethods ShowMethods
这样就能g5483到ShowMethods类的所有方法了。g4625g12661源代码里没定义
g7512造函数,g1306是清g2345里g17836是有一个public的默g16760g7512造函数。这是g13546g16805器自动g2524g6116的。g3926g7536你把这个类g6925g6116非public的(也就是package权限的),那g1052这个g2524g6116的默g16760g7512造函数就不会有了。g2524g6116出来的默g16760g7512造函数的访问权限会和类的相同。
g17836有一个有趣的实验,试试用char,int,Stringg1055类的参数去g16855用
java ShowMethods java.lang.String。
Thinking in Java 3rd Edition
g12544 23 g20041 g1861 23 g20041 www.wgqqh.com/shhgs/tij.html email:shhgs@sohu.com
这个工具能帮你省下很g3822时间。g13546程的时g1517,g3926g7536你记不起来类有没有这个方法,g6122g13785能不能这样g1363用,g8616方说Colorg4557g16949的时g1517,就g1889也不用着去翻箱倒柜地去g7609JDKg7003g7735了。
第14章g17836有一个这个程序的GUI版(专为提取Swing组件的信息而定制的),所以你g13546程的时g1517可以把这个程序打开,这样g7609起来会g7368快些。
总结
RTTI能g16765你用一个匿名的g3534类reference来获取g4557g16949的确切类型的信息。g3324不g6038g3822态方法g16855用的时g1517,这g1052做是g10714所当然的,g3252g8504新手们会自然而然的g5831到它,g1122是就用错了地方。g4557很g3822从面向过程的g13546程语言转过来的人来说,刚开g3999的时g1517,他们g17836不习惯扔掉switch语句。g1122是当他们用RTTI来g13546程的时g1517,就会错过g3822态性所带来的g13546程和代码维护方面的好g3800。Java的g7424义是g16765你g3324程序里面全程g1363用g3822态性,只是g3324万不g5483
已的情况下才g1363用RTTI。
g1306是g16213g5831正确地g1363用g3822态方法g16855用,你就必须g16213能控制g3534类的定义。g3252为当你扩展程序的时g1517,可能会发现g3534类里面没有你g5831g16213的方法。g3926g7536这个
g3534类是来自类库的,g6122是g11013别人控制的,那g1052RTTI就g6116解决方案了g726你可以继承一个新的类,然g2530g2164上你自己的方法。g3324程序的g1866它地方,你可以检测出这个类型,g16855用那些特殊的方法。这样做不会破坏g3822态性,也不影响程序的扩展性,g3252为g2164一个新的类型不会g16213你去到g3800修g6925switch语句。g1306是,g3926g7536是g3324程序的主体部分g2164入g16213g1363用新特性的代码的g16817,你就必须g1363用RTTI来检g7609g4557g16949的确切类型了。
把特性g2164入g3534类就g5859味着为了迁就某个类,所有g1866它从这个g3534类继承下来的类g18129g5483g2164上一些毫g7092g5859义的方法。这就g1363g6521口变g5483很不清晰了,而且继承的时g1517,g17836g16213把这些abstract的方法全g18129填满,这是件很烦人的事。
g8616方说,有一个表g12046乐器的类系。假设你g16213把乐团里的所有乐器的喇叭嘴全g18129清洗一遍,把clearSpitValve( )方法放进Instrumentg3534类算一个办法,g1306是这就等g1122g3324说Percussion(打击乐器)和Electronic
乐器也有喇叭嘴,所以这种做法是很浆糊的。g3324这种情况下,RTTI提供了一种g7368为g2524g10714的解决方案,g3252为你可以把这个方法放到一个g2524g17878的g4388类里面(这里就是Wind)。g1306是g7368好的办法g5224g16825是往g3534类里放一个
prepareInstrument( )方法。不过第一g8437碰到这类问题的时g1517,一般不会去这g1052g5831,你会误g16760为只能g1363用RTTI。
最g2530,RTTIg17836会被用来解决g6940率问题。假设你g1901了一个很好的g3822态程序,g1306是g17828行的时g1517发现,有个g4557g16949g2465映奇慢。g1122是,你就可以用RTTI
把这个g4557g16949拣出来,然g2530专g19388g19036g4557它的问题g1901代码以提g20652程序的g17828行g6940
率。不过g13546程的时g1517切g5536去过g7101g1260g2282代码。这是一个很有g16837g5797的g19531g19461。最好g17836是g1820g16765程序g17317起来,然g2530g1889判断一下它g17317g5483是不是g3827快了。只有g16285g5483
Chapter 10,Detecting Types
www.wgqqh.com/shhgs/tij.html email:shhgs@sohu.com g12544 24 g20041 g1861 24 g20041
它g17836不g3827快,你才g5224g16825去着手解决g6940率问题g252g252用profiler(g16277第15
章)。
练习
只g16213g1196很g4579一g12520g17165用就能从www.BruceEckel.com下g17745名为The
Thinking in Java Annotated Solution Guide的g11017g4388g7003g7735,这上面有一些习题的g12584案。
1,往Shapes.java里面g2164一个Rhomboid类。g2031g5326一个Rhomboidg4557
g16949,上g1268g13485Shape,然g2530g1889下g1268回Rhomboid。试着把它下g1268g13485
Circle,g11487g11487会有g1172g1052g13479g7536。
2,修g6925g13463习1,g3324下g1268g2081用instanceof检g7609一下g4557g16949的类型。
3,修g6925Shapes.java,g16765它能“g12373出”(设g13634一个g7643记)某种g5430g10378。Shape
的g2520个g8978g10995类的toString( )方法g16213能g7643g16794这个Shape是不是被“g12373
出”出来了。
4,修g6925SweetShop.java,用命令行参数去控制g2520种g4557g16949的g2031g5326。也就是说,g3926g7536命令行是“java SweetShop Candy”,那g1052它只g2031g5326
Candyg4557g16949。g16278g4531一下,你是g5602样g17902过命令行参数来控制Classg4557g16949
的g16025g17745的。
5,往PetCount3.java里面g2164一个新的Pet类。验g16789一下,main( )能正确地g2031g5326并且g16757算这类g4557g16949。
6,g1901一个方法,这个方法g16213能g6521g2475一个g4557g16949,然g2530g17894g5414打g2372这个g4557g16949所g4658
类系的所有类。
7,修g6925g13463习6,g16765它用Class.getDeclaredFields( )方法把类的数g6466g6116g2604
也g13485打g2372出来。
8,g6226到ToyTest.java,把Toy的默g16760g7512造函数g8892g18334掉,然g2530说g7138一下,
会有g1172g1052g13479g7536。
9,往ToyTest.java里面g2164一个interface,验g16789一下,它会被正确地检测并且g7186g12046出来。
10,g1901一个程序g16765它判断一下,char数组g12362g12467是一种primitiveg17836是一个
g17147g11507g1227实的g4557g16949。
11,g6365g10043“g5647g13479”部分所说的,实现clearSpitValve( )方法。
12,g6365g10043g7424章所g17860,实现rotate(Shape)方法,g16765它检g7609一下,g16213g7071转的g5430
g10378是不是Circle(g3926g7536是,就不进行这g20045g6817g1328)。
13,g6226到ToyTest.java,g2045用reflectiong7438制,g16855用非默g16760g7512造函数g2031g5326一个Toyg4557g16949。
Thinking in Java 3rd Edition
g12544 25 g20041 g1861 25 g20041 www.wgqqh.com/shhgs/tij.html email:shhgs@sohu.com
14,g7609g19417JDKg7003g7735的java.lang.Class部分。g1901一个程序,g16765它用Class方法,把用命令行参数g13485出的类的所有信息全g18129提取出来。用g7643g1946类库里的类,以及你自g2031的类做检验。
15,修g6925ShowMethods.java中的正则表达式,g16765它g1889g2105g12175native和
final这两个g1863g19202词(提g12046g726g1363用“g6122”g17828算符g254|g255。)