应用程序在运行的时候获取一个对象的类型信息的技术称之为运行时类型信息RTTI(Run-Time Type Identification)。RTTI是C++的一个概念,Java中的RTTI是在《think in java》这本书中作者提出来的,官方文档里面并没有RTTI这个概念。作为学习者不应该纠结于一项技术的命名,而应该关注命名所指代的具体技术或者思想,RTTI指代的是一组在运行时获取对象类型信息的方法。
为什么要在运行时获取一个对象的类型信息?
抽象编程通过基类对象来操作子类对象,怎么把基类对象转换为子类对象?考虑下面示例代码,怎么把animal对象转换为Cat或者Dog对象:
class Animal{ }
class Pet extends Animal{}
class Dog extends Pet{ }
class Cat extends Pet{ }
public class Test {
/**
* 随机创建Dog或Cat对象,共10个。
*/
static ArrayList<Animal> randomCreate() {
ArrayList<Animal> animals = new ArrayList<>();
Random r = new Random();
for (int i=0; i<10; i++) {
int num = Math.abs(r.nextInt()) % 2;
if (num == 0)
animals.add(new Dog());
else
animals.add(new Cat());
}
return animals;
}
public static void main(String[] args) {
ArrayList<Animal> animals = randomCreate();
for (Animal animal : animals) {
//怎么把animal转换为Cat或者Dog?
}
}
}
由于我们是随机创建Cat或者Dog对象,在for循环无法判断animal对象到底是Cat还是Dog,这时就需要获取对象的类型信息,根据类型信息把animal对象转换为Cat或者Dog对象。
回到问题,为什么要在运行时获取一个对象的类型信息?答案是为了类型转换,在实际开发过程中会遇到需要把基类对象转换为某个子类对象,而我们不知道基类对象所引用的对象是否真的属于这个子类,通过RTTI我们可以检测对象的具体类型然后进行转换。
获取类型信息
有三种方法可以在运行时获取类型信息:
- instanceof 关键字
- Class类的isInstance方法
- 使用equal方法比较Class是否一致
使用instanceof关键字获取类型信息使用示例如下:
ArrayList<Animal> animals = randomCreate();
for (Animal animal : animals) {
if (animal instanceof Dog) {
Dog d = (Dog) animal;
}
if (animal instanceof Cat) {
Cat c = (Cat) animal;
}
}
Class类的isInstance方法使用示例如下:
ArrayList<Animal> animals = randomCreate();
for (Animal animal : animals) {
if (Dog.class.isInstance(animal)) {
Dog d = (Dog) animal;
}
if (Cat.class.isInstance(animal)) {
Cat c = (Cat) animal;
}
}
使用equal方法比较Class是否一致使用示例如下:
ArrayList<Animal> animals = randomCreate();
for (Animal animal : animals) {
// 也可以使用==进行比较:Dog.class == animal.getClass()
if (Dog.class.equals(animal.getClass())) {
Dog d = (Dog) animal;
}
if (Cat.class.equals(animal.getClass())) {
Cat c = (Cat) animal;
}
}
instanceof关键字和isInstance方法功能上完全一模一样,下文使用instanceof指代。equals和==功能上也是一模一样,下文使用equals指代。instanceof和equals的区别在于,instanceof考虑了继承,可以判断一个对象是否派生于某个类。而equals则没有考虑继承,它只能判断对象是不是某个确切的类型。示例如下:
Animal dog = new Dog();
if (dog instanceof Pet){
System.out.println("dog instanceof Pet => true");
} else {
System.out.println("dog instanceof Pet => false");
}
if (dog.getClass().equals(Pet.class)){
System.out.println("dog.getClass().equals(Pet.class) => true");
} else {
System.out.println("dog.getClass().equals(Pet.class) => false");
}
/*程序输出
dog instanceof Pet => true
dog.getClass().equals(Pet.class) => false
*/
最后
抽象编程通过基类来操作子类对象,使模块依赖于基类,从而降低模块之间的耦合性,如果使用RTTI将使模块间的耦合性变高,所以当应用程序需要使用RTTI技术时,应该考虑是否设计出了问题,是否必须使用RTTI。