迄今为止,我们已知的RTTI形式包括:

  1. 传统的类型转换,如“(Shape)”,由RTTI确保类型转换的正确性,如果执行了一个错误的类型转换,就会抛出一个ClassCaseException。
  2. 代表对象的类型的Class对象。通过查询Class对象可以获取运行时所需的信息。

在C++中,经典的类型转换“(Shape)”并不使用RTTI。它只是简单地告诉编译器将这个对象作为新的类型对待。而java要执行类型检查,这通常被称为“类型安全的向下转型”。之所以叫“向下转型”,是由于类层次结构图从来就是这么排列的。如果将Circle类转换为Shape类型被称作向上转型,那么将Shape转型为Circle,就被称为向下转型。但是,由于知道Circle肯定是一个Shape,所以编译器允许自由地做向上转型的赋值操作,而不需要任何显式的转型操作。编译器无法知道对于给定的Shape到底是什么Shape——它可能就是Shape,或者是Shape的子类型,例如Circle、Square、Traingle或某种其他的类型。在编译期。编译器只能知道它是Shape。因此,如果不使用显式的类型转换,编译器就不允许你执行向下转型赋值,以告知编译器你拥有额外的信息,这些信息使你知道该类型是某种特定类型(编译器将检查向下转型是否合理,因此它不允许向下转型到实际上不是待转型类的子类的类型上)。

RTTI在java中还有第三种形式,就是关键字instanceof。它返回一个布尔值,告诉我们对象是不是某个特定类型的实例。可以用提问的方式使用它,就像这样:

if(x instanceof Dog)((Dog)x).bark();

在将x转型成一个Dog前,上面的if语句会检查对象x是否从属于Dog类。进行向下转型前,如果没有其他信息可以告诉你这个对像是什么类型,那么使用instanceof是非常重要的,否则会得到一个ClassCastException异常。

一般,可能想要查找某种类型(比如要找三角形,并填充成紫色),这时可以轻松地使用instanceof来计数所有对象。例如,假设你有一个类的继承体系,描述了Pet(以及他们的主人,这是在后面的示例中出现的一个非常方便的特性)。这个继承体系中的每个Individual都有一个id和一个可选名字。  尽管下面的类都继承自Individual,但是Individual类复杂性较高,此处并不需要去了解Individual的代码——你只需要了解你可以创建其具名或不具名的对象,并且每个Individual都有一个id()方法,可以返回其唯一的标识符(通过对每个对象计数而创建的)。还有一个toString()方法,如果你没有为Individual提供名字,toString()方法只产生类型名字。

/*** 个别的*/
public class Individual implements Comparable<Individual> {private static long counter = 0;private final long id = counter++;private String name;public Individual(String name) {this.name = name;}public Individual() {}public String toString() {return getClass().getSimpleName() + (name == null ? "" : "" + name);}public long id() {return id;}public boolean equals(Object o) {return o instanceof Individual && id == ((Individual) o).id;}public int hashCode() {int result = 17;if (name != null)result = 37 * result + name.hashCode();result = 37 * result + (int) id;return result;}@Overridepublic int compareTo(Individual arg) {String first = getClass().getSimpleName();String argFirst = arg.getClass().getSimpleName();int firstCompare = first.compareTo(argFirst);if (firstCompare != 0)return firstCompare;if (name != null && arg.name != null) {int secondCompare = name.compareTo(arg.name);if (secondCompare != 0)return secondCompare;}return (arg.id < id ? -1 : (arg.id == id ? 0 : 1));}
}
/*** 人*/
public class Person extends Individual {public Person(String name) {super(name);}
}
/*** 宠物*/
public class Pet extends Individual {public Pet(String name) {super(name);}public Pet() {}
}
/*** 狗*/
public class Dog extends Pet {public Dog(String name) {super(name);}public Dog() {super();}
}
/*** 杂种狗*/
public class Mutt extends Dog {public Mutt(String name) {super(name);}public Mutt() {super();}
}
/*** 哈巴狗*/
public class Pug extends Dog {public Pug(String name) {super(name);}public Pug() {super();}
}
/*** 猫*/
public class Cat extends Pet {public Cat(String name) {super(name);}public Cat() {super();}
}
/*** 埃及猫*/
public class EgyptianMau extends Cat {public EgyptianMau(String name) {super(name);}public EgyptianMau() {super();}
}
/*** 马恩岛猫*/
public class Manx extends Cat {public Manx(String name) {super(name);}public Manx() {super();}
}
/*** 威尔士猫*/
public class Cymric extends Manx {public Cymric(String name) {super(name);}public Cymric() {super();}
}
/*** 啮齿目动物*/
public class Rodent extends Pet {public Rodent(String name) {super(name);}public Rodent() {super();}
}
/*** 鼠*/
public class Rat extends Rodent {public Rat(String name) {super(name);}public Rat() {super();}
}
/*** 老鼠*/
public class Mouse extends Rodent {public Mouse(String name) {super(name);}public Mouse() {super();}
}
/*** 仓鼠*/
public class Hamster extends Rodent {public Hamster(String name) {super(name);}public Hamster() {super();}
}

接下来,我们需要一种方法,通过它可以随机地创建不同类型的宠物,并且为方便起见,还可以创建宠物数组和List。为了使该工具能够适应多种不同的实现,我们将其定义为抽象类:

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Random;public abstract class PetCreator {private Random r = new Random();public abstract List<Class<? extends Pet>> types();public Pet randomPet() {int n = r.nextInt(types().size());try {return types().get(n).newInstance();} catch (InstantiationException e) {throw new RuntimeException(e);} catch (IllegalAccessException e) {throw new RuntimeException(e);}}public Pet[] createArray(int size) {Pet[] result = new Pet[size];for (int i = 0; i < size; i++)result[i] = randomPet();return result;}public ArrayList<Pet> arrayList(int size) {ArrayList<Pet> result = new ArrayList<>();Collections.addAll(result, createArray(size));return result;}
}

抽象的getType()方法在导出类中实现,以获取由Class对象构成的List(这是模版方法设计模式的一种变体)。注意,其中类的类型被指定为“任何从Pet导出的类”,因此newInstance()不需要转型就可以产生Pet。randomPet()随机地产生List中的索引,并使用被选取的Class对象,通过Class.newInstance()来生成该类的新实例。createArray()方法使用randomPet()来填充数组,而ArrayList()方法使用的则是createArray()。

在调用newInstance()时,可能会得到两种异常,在紧跟try语句块后面的catch子句中可以看到对它们的处理。异常的名字再次成为了一种对错误类型相对比较有用的解释(IllegalAccessException表示违反了java安全机制,在本例中,表示默认构造器为private的情况)。

当你导出PetCreator的子类时,唯一所需提供的就是你希望使用randomPet()和其他方法来创建的宠物类型的List。getTypes()方法通常只返回对一个静态List的引用。下面是使用forName()的一个具体实现:

import java.util.ArrayList;
import java.util.List;public class ForNameCreator extends PetCreator {private static List<Class<? extends Pet>> types = new ArrayList<>();private static String[] typeNames = { "typeinfo.pets.Mutt", "typeinfo.pets.Pug", "typeinfo.pets.EgyptianMau","typeinfo.pets.Manx", "typeinfo.pets.Cymric", "typeinfo.pets.Rat", "typeinfo.pets.Mouse","typeinfo.pets.Hamster" };@SuppressWarnings("unchecked")private static void loader() {try {for (String name : typeNames)types.add((Class<? extends Pet>) Class.forName(name));} catch (ClassNotFoundException e) {throw new RuntimeException(e);}}static {loader();}@Overridepublic List<Class<? extends Pet>> types() {return types;}
}

loader()方法用Class.forName()创建了Class对象的List,这可能会产生ClassNotFoundException异常,这么做是有意义的,因为你传递给它的是一个在编译期无法验证的String。由于Pet对象在typeinfo包中,因此必须使用报名来引用这些类。

为了产生具有实际类型的Class对象的List,必须使用转型,这会产生编译期警告。loader()方法被单独定义,然后被置于一个静态初始化子句中,因为@SuppressWarnings注解不能直接置于静态初始化子句之上。

为了对Pet进行计数,我们需要一个能跟踪各种不同类型的Pet的数量的工具。Map是此需求的首选,其中键是Pet类型名,而值是保存Pet数量的Integer。通过这种方式,你可以询问:“有多少个Hamster对象?”我们可以使用instanceof来对Pet进行计数:

import java.util.HashMap;import typeinfo.pets.*;public class PetCount {static class PetCounter extends HashMap<String, Integer> {public void count(String type) {Integer quantity = get(type);if (quantity == null)put(type, 1);elseput(type, quantity + 1);}}public static void countPets(PetCreator creator) {PetCounter counter = new PetCounter();for (Pet pet : creator.createArray(20)) {System.out.print(pet.getClass().getSimpleName() + " ");if (pet instanceof Pet)counter.count("Pet");if (pet instanceof Dog)counter.count("Dog");if (pet instanceof Mutt)counter.count("Mutt");if (pet instanceof Pug)counter.count("Pug");if (pet instanceof Cat)counter.count("Cat");if (pet instanceof Manx)counter.count("EgyptianMau");if (pet instanceof Manx)counter.count("Manx");if (pet instanceof Manx)counter.count("Cymric");if (pet instanceof Rodent)counter.count("Rodent");if (pet instanceof Rat)counter.count("Rat");if (pet instanceof Mouse)counter.count("Mouse");if (pet instanceof Hamster)counter.count("Hamster");}System.out.println();System.out.println(counter);}public static void main(String[] args) {countPets(new ForNameCreator());}
}

在CountPets()中,是使用PetCreator来随机地向数组中填充Pet的。然后使用instanceof对该数组中的每个Pet进行测试和计数。

对instanceof有比较严格的限制,只可将其与命名类型进行比较,而不能与Class对象作比较。在前面的例子中,可能觉得写出那么一大堆instanceof表达式是很乏味的,的确如此。但是也没有办法让instanceof聪明起来,让它能够自动地创建一个Class对象的数组,然后将目标对象与这这个数组中的对象进行逐一的比较(稍后你会看到一个替代方案)。其实这并非是一种如你想象中那般好的限制,因为渐渐的就会理解,如果程序中编写了许多的instanceof表达式,就说明你的设计可能存在瑕疵。

如果本文对您有很大的帮助,还请点赞关注一下。

类型信息(3):类型转换前检查(上)相关推荐

  1. 14章类型信息之使用类字面常量

    14章类型信息-之类型转换前先做检查--之使用类字面常量--类名.class--以及动态instanceof(isInstance方法)----递归计数(计算各个类的个数) 实例代码: 实体类父类: ...

  2. 深入理解Java类型信息(Class对象)与反射机制

    关联文章: 深入理解Java类型信息(Class对象)与反射机制 深入理解Java枚举类型(enum) 深入理解Java注解类型(@Annotation) 深入理解Java并发之synchronize ...

  3. thinking-in-java(14)类型信息

    [0]开场白 1)运行时类型信息使得你可以在程序运行时发现和使用类型信息: 2)java是如何在运行时识别对象和类信息的?两种方式: 方式1)传统的RTTI(RunTime Type Identifi ...

  4. Think in Java第四版 读书笔记8第14章 类型信息(RTTI与反射)

    Java如何在运行时识别对象和类的信息? 1.RTTI(Run-time type information) 它假定我们在编译时已经知道了所有类型 2.反射 它允许我们在运行时发现和使用类的信息 14 ...

  5. 《Java编程思想》笔记14.类型信息

    运行时类型信息使得你可以在运行时发现和使用类型信息,主要有两种方式: "传统的"RTTI,它假定我们在编译时已经知道了所有的类型: "反射"机制,它允许我们在运 ...

  6. Java编程思想--14类型信息

    第十四章类型信息 14.1 为什么需要RTTI 14.2 Class对象 Class对象 Class.forName(String s) 14.2.1字面类常量 14.2.2 泛化Class的引用 1 ...

  7. 【笔记】《Java编程思想(第四版)》第14章-类型信息

    第14章 类型信息 RTTI(Run-Time Type Identification)运行阶段类型识别 运行时类型信息使得你可以在程序运行时发现和适用类型信息. 一种是"传统的" ...

  8. thinking in java 学习笔记 14 类型信息

    第十四章 类型信息 尼玛,刚刚看完了亚冠,恒大这样都被连扳3球,尼玛的垃圾孙祥,恨死了那个全北现代 好吧,回到学习上 运行时类型信息使得你可以再程序运行时发现和使用类型信息 本章讨论的是java如何让 ...

  9. 「深入Java」类型信息:RTTI和反射

    1.RTTI Run-Time Type Infomation 运行时类型信息 为什么需要RTTI? 越是优秀的面向对象设计,越是强调高内聚低耦合,正如依赖倒转原则所说:"无论是高层模块还是 ...

最新文章

  1. 基于GraphHopper搭建离线路径规划服务并可视化
  2. 你从未听说过的最重要的数据库,人类登月计划的功臣
  3. 笔试训练1 知识点整理
  4. Anton Chuvakin:关于日志管理产品的十个注意事项
  5. 第一章 Shiro简介——《跟我学Shiro》
  6. Python《爬虫初实践》
  7. kitti数据集_神秘的Waymo一反常态,CVPR现场发布大型自动驾驶数据集
  8. 10_文件包含漏洞(属于任意代码执行)
  9. python简单文件读写
  10. python核心理念_《三天搞定Python基础概念之第一天》中文版
  11. jmeter性能测试用户参数参数化
  12. 【王道计组笔记】输入/输出设备磁盘
  13. hdu 3221 Brute-force Algorithm(高速幂取模,矩阵高速幂求fib)
  14. mysql jdbc8.0驱动包下载_JDBC驱动jar包|JDBC驱动(mysql connector java)下载v8.0.11安装包 - 欧普软件下载...
  15. 2015年最新中国知网CNKI免费账号直接入口
  16. ECU软件开发介绍篇
  17. 动态gif图如何裁剪?一个小窍门教你在线裁剪动图
  18. 机敏问答[复变][5] #20210629
  19. ORA-39083 ORA-10615
  20. SCAP标准协议和威胁情报关键词术语

热门文章

  1. javas函数(三)闭包函数
  2. 第七章 凹凸映射 渐变纹理 遮罩纹理
  3. QT对话框延迟关闭销毁
  4. 4.Jenkins入门基础使用与邮箱钉钉微信消息通知集成配置与实践
  5. c++ dp 贪婪的戈尔曼题解
  6. Python数组list操作
  7. html css 奥运五环,CSS3 奥运五环加载动画
  8. 个人感兴趣游戏本信息汇总_2018年4月
  9. 机械师t58java编程如何_机械师t58怎么样?
  10. Centos7普通用户安装Hadoop