刚写Java的时候,以为类型转换是万能的,比如使用(Shape)这种方式进行强制转换,若真是万能的,则再好不过了,可是现实却给了无数次ClassCastException的打击。默然回首,才发现最好在类型转换前做一下检查方位上策。
类型转换前先做检查
编译器允许你自由的做向上转型的赋值操作,而不需要任何显示的转型操作,就好像给超类的引用赋值那样。
然而如果不使用显示的类型转换,编译器就不允许你执行向下转换赋值,这个时候我们不妨先来检查一下对象是不是某个特定类型的实例,使用到了关键字 instanceof:
if(x instanceof Dog)
((Dog) x).bark();
RTTI的形式:
所以,到目前为止,我们知道RTTI的形式包括:
1、传统的类型转换 (Shape)
2、代表对象的类型的Class对象
3、关键字instanceof
动态的instanceof方法:
Class.isInstance方法提供给了一种动态测试对象的途径。
下面演示下instanceof和Class.isInstance的用法:
Attribute:
public interface Attribute {
}
Shape:
/**
- 创建一个抽象类
*/
public abstract class Shape{
// this调用了当前类的toString方法获得信息
public void draw() { System.out.println(this + “.draw()”); }
// 声明toString()方法为abstract,从而强制继承者需要重写该方法。
abstract public String toString();
}
Circle:
public class Circle extends Shape implements Attribute{
public String toString(){ return “Circle”; }
}
Square:
public class Square extends Shape{
public String toString(){ return “Square”; }
}
Triangle:
public class Triangle extends Shape{
public String toString(){ return “Triangle”; }
}
类型检查:
// instanceOf
Circle c = new Circle();
// 判断是否超类的实例
System.out.format(“Using instanceof: %s is a shape? %b\n”,
c.toString(), c instanceof Shape);
// 判断是否Circle的实例
System.out.format(“Using instanceof: %s is a circle? %b\n”,
c.toString(), c instanceof Circle);
// 判断是否超类的实例
System.out.format(“Using Class.isInstance: %s is a shape? %b\n”,
c.toString(), Shape.class.isInstance©);
// 判断是否接口的实例
System.out.format(“Using Class.isInstance: %s is a Attribute? %b\n”,
c.toString(), Attribute.class.isInstance©);
可以发现,instanceof或者Class.isInstance方法判断了是否继承体系的实例,即除了判断本身,还判断是否超类或接口的实例。
下面演示下使用动态的Class.instance的用法:
首先创建一个抽象的形状生成器类:
public abstract class ShapeCreator {
private Random rand = new Random(10);
// 返回一个对象类型数组,由实现类提供,后面会看到两种实现形式,基于forName的和基于类字面常量的.class
public abstract List<Class<? extends Shape>> types();
// 随机生成一个对象类型数组中的类型对象实例
public Shape randomShape(){
int n = rand.nextInt(types().size());
try {
return types().get(n).newInstance();
} catch (InstantiationException e) {
e.printStackTrace();
return null;
} catch (IllegalAccessException e) {
e.printStackTrace();
return null;
}
}
// 生成一个随机数组
public Shape[] createArray(int size){
Shape[] result = new Shape[size];
for(int i=0; i<size; i++){
result[i] = randomShape();
}
return result;
}
// 生成一个随机数组,泛型的ArrayList
public ArrayList
ArrayList
Collections.addAll(result, createArray(size));
return result;
}
}
接下来编写一个该抽象类的实现:
/**
- forName的生成器实现
- @author arthinking
*/
public class ForNameCreator extends ShapeCreator{
private static List<Class<? extends Shape>> types =
new ArrayList<Class<? extends Shape>>();
private static String[] typeNames = {
"com.itzhai.javanote.entity.Circle",
"com.itzhai.javanote.entity.Square",
"com.itzhai.javanote.entity.Triangle"
};
@SuppressWarnings("unused")
private static void loader(){
for(String name : typeNames){
try {
types.add((Class<? extends Shape>)Class.forName(name));
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
// 初始化加载所需的类型数组
static {
loader();
}
public List<Class<? extends Shape>> types() {
return types;
}
}
最后写一个统计形状个数的类,里面用到了instanceof:
public class ShapeCount {
static class ShapeCounter extends HashMap<String, Integer>{
public void count(String type){
Integer quantity = get(type);
if(quantity == null){
put(type, 1);
} else {
put(type, quantity + 1);
}
}
}
// 演示通过instanceof关键字统计对象类型
public static void countShapes(ShapeCreator creator){
ShapeCounter counter = new ShapeCounter();
for(Shape shape : creator.createArray(20)){
if(shape instanceof Circle)
counter.count("Circle");
if(shape instanceof Square)
counter.count("Square");
if(shape instanceof Triangle){
counter.count("Triangle");
}
}
System.out.println(counter);
}
public static void main(String[] args){
countShapes(new ForNameCreator());
}
}
改写一下抽象类的实现,重新用类字面常量实现:
/**
- 字面量的生成器实现
- @author arthinking
*/
public class LiteralCreator extends ShapeCreator{
public static final List<Class<? extends Shape>> allType =
Collections.unmodifiableList(Arrays.asList(Circle.class, Triangle.class, Square.class));
public List<Class<? extends Shape>> types(){
return allType;
}
public static void main(String[] args){
System.out.println(allType);
}
}
现在使用Class.instance统计形状的个数如下:
/**
- 通过使用Class.instanceof动态的测试对象,移除掉原来的ShapeCount中单调的instanceof语句
- @author arthinking
*/
public class ShapeCount2 {
private static final List<Class<? extends Shape>> shapeTypes = LiteralCreator.allType;
static class ShapeCounter extends HashMap<String, Integer>{
public void count(String type){
Integer quantity = get(type);
if(quantity == null){
put(type, 1);
} else {
put(type, quantity + 1);
}
}
}
// 演示通过Class.isInstance()统计对象类型
public static void countShapes(ShapeCreator creator){
ShapeCounter counter = new ShapeCounter();
for(Shape shape : creator.createArray(20)){
for(Class<? extends Shape> cls : shapeTypes){
if(cls.isInstance(shape)){
counter.count(cls.getSimpleName());
}
}
}
System.out.println(counter);
}
public static void main(String[] args){
countShapes(new ForNameCreator());
}
}
现在生成器有了两种实现,我们在这里可以添加一层外观,设置默认的实现方式:
/**
- 现在生成器有了两种实现,我们在这里添加一层外观,设置默认的实现方式
- @author arthinking
*/
public class Shapes {
public static final ShapeCreator creator =
new LiteralCreator();
public static Shape randomShape(){
return creator.randomShape();
}
public static Shape[] createArray(int size){
return creator.createArray(size);
}
public static ArrayList<Shape> arrayList(int size){
return creator.arrayList(size);
}
}
instanceof与Class的等价性:
instanceof和isInstance()生成的结果完全一样,保持了类型的概念,判断是否一个类或者是这个类的派生类。
equals()与==也是一样的,而使用这个比较实际的Class对象,就没有考虑继承。
System.out.println(new Circle() instanceof Circle); // true
System.out.println(Shape.class.isInstance(new Circle())); // true
System.out.println((new Circle()).getClass() == Circle.class); // true
System.out.println((new Circle().getClass()).equals(Shape.class)); // false