Java笔记 – RTTI 类型检查 instanceof
本文由发表于4年前 | J2EE | 暂无评论 |  被围观 2,939 views+
类型转换前先做检查RTTI的形式:动态的instanceof方法:instanceof与Class的等价性:

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<Shape> arrayList(int size){
        ArrayList<Shape> result = new ArrayList<Shape>();
        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
除了文章中有特别说明,均为IT宅原创文章,转载请以链接形式注明出处。
本文链接:http://www.itzhai.com/java-notes-rtti-type-checking-instanceof.html
关键字: ,
arthinking Java技术交流群:280755654,入门群:428693174 more
分享到:
 
2013 12/24
如果您有更好的原创技术博文或者观点,欢迎投稿:admin@itzhai.com,或者关注订阅左侧浮动面板的微信号订阅IT宅itread)发送消息。
文章评论
    没有评论
给我留言

有人回复时邮件通知我
J2EE的相关文章
随机文章 本月热门 热评
1 C++语法笔记 – 继承与派生 2011/9/3
2 Struts2笔记 – 参数的接收 使用DomainObject和ModelDriven 2011/6/18
3 树形组建TreePanel从servlet中读取JSON数据 2011/4/10
4 C语言语法笔记汇总 | IT宅文章归档 AD 2011/11/14 2011/11/14
5 Java基础笔记 – 抽象类与接口 2011/10/26
6 Struts2单选按钮标签s:radio的使用及其设置默认值 2011/9/10
友情推荐 更多
破博客 文官洗碗安天下,武将打怪定乾坤。多么美好的年代,思之令人泪落。
Mr.5's Life 白天是一名程序员,晚上就是个有抱负的探索者
行知-追寻技术之美 关注大数据,分布式系统
我爱编程 编程成长轨迹
Cynthia's Blog 学习笔记 知识总结 思考感悟
 
猜您喜欢
欢迎关注我的公众号 IT宅
关于IT宅 文章归档

IT宅中的文章除了标题注明转载或有特别说明的文章,均为IT宅的技术知识总结,学习笔记或随笔。如果喜欢,请使用文章下面提供的分享组件。转载请注明出处并加入文章的原链接。 感谢大家的支持。

联系我们:admin@itzhai.com

Theme by arthinking. Copyright © 2011-2015 IT宅.com 保留所有权利.