第十一章的标题为运行期类型鉴定,其实实际就是关于自动下塑的进一步讨论,因为很多时候我们会遇到到多个子类由同一个父类派生,然后创建了一个父类句柄的容器,它装载了各种子类的对象,如果我们要找出其中某一子类的全部对象,然后调用仅那个的子类所具有的方法。这看起来用一般的下塑方法解决起来会很麻烦,如果使用一般的下塑方法我们可能让父类包含一个具有所有子类名字的枚举类型的对象。然后用switch语句来进行判断类型以及下塑。但是这样的话在添加新子类的时候就必须修改枚举类型以及程序代码。这样会使得程序变得几乎难以修改。但是如果使用这章里面所提到的方法能对上述情况有所改善。
这一张最开始使用的方法是Class.forName(String classname)以及instanceof,其中代表对象类型的Class 对象。可查询Class 对象,获取有用的运行期资料。instanceof则是检查两种类型是否兼容,然后返回一个boolean值。一下列出书中给出的代码,阅读以后应该可以明白以上两种方法的使用,并且能对一开始提出的例子得出更方便的解决办法。
package c11.petcount;
import java.util.*;
class Pet {}
class Dog extends Pet {}
class Pug extends Dog {}
class Cat extends Pet {}
class Rodent extends Pet {}
class Gerbil extends Rodent {}
class Hamster extends Rodent {}
class Counter { int i; }
public class PetCount
{
static String[] typenames = {
"Pet", "Dog", "Pug", "Cat",
"Rodent", "Gerbil", "Hamster",
};
public static void main(String[] args)
{
Vector pets = new Vector();
try
{
Class[] petTypes =
{
Class.forName("Dog"),
Class.forName("Pug"),
Class.forName("Cat"),
Class.forName("Rodent"),
Class.forName("Gerbil"),
Class.forName("Hamster"),
};
for(int i = 0; i < 15; i++)
pets.addElement(petTypes[(int)(Math.random()*petTypes.length)].newInstance());
}
catch(InstantiationException e) {}
catch(IllegalAccessException e) {}
catch(ClassNotFoundException e) {}
Hashtable h = new Hashtable();
for(int i = 0; i < typenames.length; i++)
h.put(typenames[i], new Counter());
for(int i = 0; i < pets.size(); i++)
{
Object o = pets.elementAt(i);
if(o instanceof Pet)
((Counter)h.get("Pet")).i++;
if(o instanceof Dog)
((Counter)h.get("Dog")).i++;
if(o instanceof Pug)
((Counter)h.get("Pug")).i++;
if(o instanceof Cat)
((Counter)h.get("Cat")).i++;
if(o instanceof Rodent)
((Counter)h.get("Rodent")).i++;
if(o instanceof Gerbil)
((Counter)h.get("Gerbil")).i++;
if(o instanceof Hamster)
((Counter)h.get("Hamster")).i++;
}
for(int i = 0; i < pets.size(); i++)
System.out.println(pets.elementAt(i).getClass().toString());
for(int i = 0; i < typenames.length; i++)
System.out.println(typenames[i] + " quantity: " + ((Counter)h.get(typenames[i])).i);
}
}
其实从上面的程序来看实际上实现下塑的是if(o instanceof classname)语句,Class.forName(String classname)只是为了方便实现随机生成子类而使用的。不过虽然使用instanceof虽然方便了不少但是仍旧存在添加新的类型时必须修改代码的问题,而且其中大量的if语句也让人觉得进行了大量的重复编程,所以书中又使用了isInstance()方法来代替instanceof方法,以至于其中的大量if语句能用一个for循环来控制,同时Class.forName(String classname)也被classname.class这个更方便的方法代替。给出代码如下:
package c11.petcount;
import java.util.*;
class Pet {}
class Dog extends Pet {}
class Pug extends Dog {}
class Cat extends Pet {}
class Rodent extends Pet {}
class Gerbil extends Rodent {}
class Hamster extends Rodent {}
class Counter { int i; }
public class PetCount
{
public static void main(String[] args)
{
Vector pets = new Vector();
Class[] petTypes =
{
Pet.class,
Dog.class,
Pug.class,
Cat.class,
Rodent.class,
Gerbil.class,
Hamster.class,
};
try
{
for(int i = 0; i < 15; i++)
{
int rnd = 1 + (int)(Math.random() * (petTypes.length - 1));
pets.addElement(petTypes[rnd].newInstance());
}
}
catch(InstantiationException e) {}
catch(IllegalAccessException e) {}
Hashtable h = new Hashtable();
for(int i = 0; i < petTypes.length; i++)
h.put(petTypes[i].toString(),
new Counter());
for(int i = 0; i < pets.size(); i++)
{
Object o = pets.elementAt(i);
for (int j = 0; j < petTypes.length; ++j)
if (petTypes[j].isInstance(o))
{
String key = petTypes[j].toString();
((Counter)h.get(key)).i++;
}
}
for(int i = 0; i < pets.size(); i++)
System.out.println(
pets.elementAt(i).getClass().toString());
Enumeration keys = h.keys();
while(keys.hasMoreElements())
{
String nm = (String)keys.nextElement();
Counter cnt = (Counter)h.get(nm);
System.out.println(nm.substring(nm.lastIndexOf('.') + 1) + " quantity: " + cnt.i);
}
}
}
这样做就解决了所有的问题,包括为了扩展而添加新的子类的问题,对于上面的代码,添加新的子类只需要在程序开头的Class数组中进行添加新的子类就可以,其他的程序保持原样仍能正常使用。
之后的几节则是针对Class这个类的深入讨论,包括提取类的方法,字段,继承接口,继承类等等,这些方法都能在API的java.lang.Class<T> 中查到。