RTTI 的简单应用 -- 《JAVA编程思想》45

RTTI (Run-Time Type Identification)指程序在运行期间获取对象的类型信息。

我们为什么需要使用 RTTI 呢?

在此之前,先来看一个多态的例子:

abstract class Shape {
    
    
    
    void draw() {
    
    
        System.out.println(this + ".draw()");
    }

    abstract public String toString();
    
}
public class Circle extends Shape {
    
    
    
    @Override
    public String toString() {
    
    
        return "Circle";
    }

    public void circle() {
    
    
        System.out.println("circle method");
    }
    
}
public class Square extends Shape {
    
    

    @Override
    public String toString() {
    
    
        return "Square";
    }

    public void square() {
    
    
        System.out.println("square method");
    }
    
}

public class Triangle extends Shape {
    
    

    @Override
    public String toString() {
    
    
        return "Triangle";
    }

    public void triangle() {
    
    
        System.out.println("triangle method");
    }

}
public class Shapes {
    
    
    
    public static void main(String[] args) {
    
    
        List<Shape> shapeList = Arrays.asList(new Circle(), new Square(), new Triangle());
        for (Shape shape : shapeList) {
    
    
            shape.draw();
        }
    }
    
}
Circle.draw()
Square.draw()
Triangle.draw()

Shape 为 Circle 、Square、Triangle 的基类,代码只对基类的引用进行操作,即使添加新的派生类也无需修改代码。

但上述例子也带来了一个问题:派生类都是通过向上转型成 Shape 存入 List 容器中,从容器中取出对象时,默认也是转换为基类 Shape 类型,故派生类的具体类型会丢失。

倘若我们现在需要根据泛化后 Shape 的具体类型调用不同的方法:Circle 调用 circle()、Square 调用 square() 、Triangle 调用 triangle(),这时 RTTI 就派生用场了,我们可以根据 Class 对象获取对象运行时的各类信息。

接下来,简单说明 RTTI 的工作原理:

每当我们编写并编译一个新类,就会产生一个 Class 对象(保存在同名的 .class 文件中),所有的对象都是基于同名的 Class 对象去创建的。

当程序创建一个对类静态成员的引用时(构造方法也是静态方法,new() 操作符创建对象也被认为是对静态成员的引用),JAVA 虚拟机(JVM)会使用 “类加载器” 去加载 class 文件,一旦某个类的Class 对象被载入内存,它就被用于创建这个类的所有对象。

因此,JAVA 程序并不是在程序开始运行的时候就加载所有对象,其各个部分是在必需时才进行加载

可以通过下面这个案例来证明:所有的类都是动态加载的。

Class.forName(String className) 在指定某个类的全路径后,可以手动加载其 Class 对象,若加载失败会抛出 ClassNotFoundException 异常。这里更推荐使用类字面常量(.class)的方式去获取 Class 对象的引用(可以参考:类字面常量 .class)。

public class Candy {
    
    
    static {
    
    
        System.out.println("Loading Candy");
    }
}
public class Gum {
    
    
    static {
    
    
        System.out.println("Loading Gum");
    }
}
public class Cookie {
    
    
    static {
    
    
        System.out.println("Loading Cookie");
    }
}
public class SweetShop {
    
    

    static {
    
    
        System.out.println("Loading SweetShop");
    }

    public static void main(String[] args) {
    
    
        System.out.println("准备加载 Candy");
        new Candy();
        System.out.println("准备加载 Gum");
        try {
    
    
            Class.forName("mtn.baymax.charpter14.Gum");
        } catch (ClassNotFoundException e) {
    
    
            System.out.println("未找到 Gum 类");
        }
        System.out.println("准备加载 Cookie");
        new Cookie();
    }

}
Loading SweetShop
准备加载 Candy
Loading Candy
准备加载 Gum
Loading Gum
准备加载 Cookie
Loading Cookie

进入正题,现在来看看 Class 对象能让我们获取到哪些类型信息:

(1)getName()、getCanonicalName()
获取 Class 的全限定类名(包含包名)

(2)getSimpleName()
获取 Class 的类名(不包含包名)

(3)isInterface()
判断该类是否为接口 interface

(4)getInterfaces()
获得该类所有实现接口的 Class 对象

(5)getSuperclass()
获得继承类的 Class 对象

(6)newInstance()
调用该类的无参构造方法实例化对象(若未提供无参构造方法,会抛出 InstantiationException 异常)

再结合实例,具体了解各类方法的使用情况:

public interface HasBatteries {
    
    }
public interface Shoots {
    
    }
public interface Waterproof {
    
    }
public class Toy {
    
    }
public class FancyToy extends Toy implements HasBatteries, Waterproof, Shoots {
    
    }
public class ToyTest {
    
    

    public static void printInfo(Class cc) {
    
    
        System.out.println("Class Name: " + cc.getName());
        System.out.println("is interface? " + "[" + cc.isInterface() + "]");
        System.out.println("Simple Name: " + cc.getSimpleName());
        System.out.println("Canonical Name: " + cc.getCanonicalName());
    }

    public static void main(String[] args) {
    
    
        Class c = null;
        try {
    
    
            c = Class.forName("mtn.baymax.charpter14.FancyToy");
        } catch (ClassNotFoundException e) {
    
    
            System.out.println("无法加载指定类");
            System.exit(1);
        }
        printInfo(c);
        for (Class face : c.getInterfaces()) {
    
    
            printInfo(face);
        }
        Class up = c.getSuperclass();
        Object obj = null;
        try {
    
    
            obj = up.newInstance();
        } catch (InstantiationException e) {
    
    
            System.out.println("此类未提供无参构造方法,实例化失败");
            System.exit(1);
        } catch (IllegalAccessException e) {
    
    
            System.out.println("无此类的访问权限反射失败");
            System.exit(1);
        }
        printInfo(obj.getClass());
    }

}
Class Name: mtn.baymax.charpter14.FancyToy
is interface? [false]
Simple Name: FancyToy
Canonical Name: mtn.baymax.charpter14.FancyToy
Class Name: mtn.baymax.charpter14.HasBatteries
is interface? [true]
Simple Name: HasBatteries
Canonical Name: mtn.baymax.charpter14.HasBatteries
Class Name: mtn.baymax.charpter14.Waterproof
is interface? [true]
Simple Name: Waterproof
Canonical Name: mtn.baymax.charpter14.Waterproof
Class Name: mtn.baymax.charpter14.Shoots
is interface? [true]
Simple Name: Shoots
Canonical Name: mtn.baymax.charpter14.Shoots
Class Name: mtn.baymax.charpter14.Toy
is interface? [false]
Simple Name: Toy
Canonical Name: mtn.baymax.charpter14.Toy

最后让我们回归开头的问题:判断泛化后的 Shape 的具体类型,调用具体类型的不同方法。

稍许改造代码,便能实现需求。

public class Shapes {
    
    

    public static void main(String[] args) {
    
    
        List<Shape> shapeList = Arrays.asList(new Circle(), new Square(), new Triangle());
        for (Shape shape : shapeList) {
    
    
            shape.draw();
            String className = shape.getClass().getName();
            switch (className){
    
    
                case "mtn.baymax.charpter14.Circle":
                    Circle circle=(Circle)shape;
                    circle.circle();
                    break;
                case "mtn.baymax.charpter14.Square":
                    Square square=(Square)shape;
                    square.square();
                    break;
                case "mtn.baymax.charpter14.Triangle":
                    Triangle triangle=(Triangle)shape;
                    triangle.triangle();
                    break;
            }
        }
    }

}
Circle.draw()
circle method
Square.draw()
square method
Triangle.draw()
triangle method

本次分享至此结束,希望本文对你有所帮助,若能点亮下方的点赞按钮,在下感激不尽,谢谢您的【精神支持】。

若有任何疑问,也欢迎与我交流,若存在不足之处,也欢迎各位指正!

おすすめ

転載: blog.csdn.net/BaymaxCS/article/details/119322362