Java笔记 - RTTI 类型检查 instanceof

20131227-java002

刚写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(c));
// 判断是否接口的实例
System.out.format(“Using Class.isInstance: %s is a Attribute? %b\n”,
c.toString(), Attribute.class.isInstance(c));

可以发现,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(int size){
ArrayList result = new 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

arthinking wechat
欢迎关注itzhai公众号