19.类型信息

类型信息

RTTI(RunTime Type Information,运行时类型信息)能够在程序运行时发现和使用类型信息。

19.1为什么需要RTTI

在java中,所有类型转换的正确性检查都是在运行时进行的。这也正是RTTI的含义所在:在运行时,识别一个对象的类型。

19.2Class对象

要理解RTTI在java中的工作原理,首先必须知道类型信息在运行时是如何表示的。这项工作是由称为Class对象的特殊对象完成的,它包含了与类有关的信息。
实际上,Class对象就是用来创建该类所有常规对象的。Java使用Class对象来实现RTTI,即便是类型转换这样的操作都是用Class对象实现的。
类是程序的一部分,每个类都有一个Class对象。换言之,每当编写并且编译了一个新类,就会产生一个Class对象(更恰当的说,是被保存在一个同名的.class文件中)。为了生成这个类的对象,java虚拟机会先调用类加载器子系统把这个类加载到内存中。
类加载器子系统可能包含一条类加载器链,但有且只有一个原生类加载器,它是JVM实现的一部分。原生类加载器加载的是可信类(包括java API类)。它们通常是从本地盘加载的。在这条链中,通常不需要添加额外的类加载器,但是如果有特殊需求(例如以某种特殊的方式加载类,以支持web服务器应用,或者通过网络下载类),也可以挂载额外的类加载器。
所有的类都是第一次使用时动态加载到JVM中的,当程序创建第一个对类的静态成员的引用时,就会加载这个类。其实构造器也是类的静态方法,虽然构造器前面并没有static关键字。所以,使用new操作符创建类的新对象,这个操作也算作对类的静态成员引用。
因此,java程序在它开始运行之前并没有被完全加载,很多部分是在需要时才会加载
类加载器首先会检查这个类的Class对象是否已经加载,如果尚未加载,默认的类加载器就会根据类名查找.class文件(如果有附加的类加载器,这时候可能就会在数据库中或者通过其它方式获得字节码)。这个类的字节码被加载后,JVM会对其进行验证,确保它没有损坏,并且不包含不良的java代码。
一旦某个类的Class对象被载入内存,它就可以用来创建这个类的所有对象。

// 检查类加载器的工作方式
public class SweetShop {
    public static void main(String[] args) {
        System.out.println("inside main");
        new Candy();
        System.out.println("After creating Candy");
        try {
            Class.forName("Gum");
        } catch (ClassNotFoundException e) {
            System.out.println("Couldn't find Gum");
        }
        System.out.println("After Class.forName(\"Gum\")");
        new Cookie();
        System.out.println("After creating Cookie");
    }
}

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

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

class Candy {
    static {
        System.out.println("Loading Candy");
    }
}
// 输出结果:
// inside main
// Loading Candy
// After creating Candy
// Loading Gum
// After Class.forName("Gum")
// Loading Cookie
// After creating Cookie
19.2.1类字面常量

Java还提供了另一种方法来生成类对象的引用:类字面常量,例如:FancyToy.class。这样做不仅更简单,而且更安全,因为它在编译时就会受到检查(因此不必放在try语句块中)。并且它根除了对forName()方法的调用,所以效率更高
类字面常量不仅可以应用于普通类,也可以应用于接口、数组以及基本数据类型。另外,对于基本数据类型的包装类,还有一个标准字段TYPETYPE字段是一个引用,指向对应的基本数据类型的Class对象。
需要注意的是,当使用.class来创建对Class对象的引用时,不会自动地初始化该Class对象。为了使用类而做的准备工作实际包含三个步骤:

  1. 加载,这是由类加载器执行的。该步骤将查找字节码(通常在classpath所指定的路径中查找,但这并非是必须的),并从这些字节码中创建一个Class对象。
  2. 链接。在链接阶段将验证类中的字节码,为static字段分配存储空间,并且如果需要的话,将解析这个类创建的对其他类的所有引用。
  3. 初始化。如果该类具有超类,则先初始化超类,执行static初始化器和static初始化块。

直到第一次引用一个static方法(构造器隐式地是static)或者非常量的static字段,才会进行类初始化。

public class ClassInitialization {
    public static Random rand = new Random(47);

    public static void main(String[] args) throws Exception {
        // 初始化有效地实现了尽可能的惰性,仅使用.class语法来获得对类对象的引用不会引发初始化
        Class initable = Initable.class;
        System.out.println("After creating Initable ref");
        // Does not trigger initialization
        // 如果一个static final值是编译期常量,那么这个值不需要对类进行初始化就可以被读取
        System.out.println(Initable.STATIC_FINAL);
        // Does trigger initialization
        // 但是,如果只是将一个字段设置成为static和final,还不足以确保这种行为
        System.out.println(Initable.STATIC_FINAL2);
        // Does trigger initialization
        System.out.println(Initable2.staticNonFinal);
        // 使用Class.forName()会立即进行初始化
        Class initable3 = Class.forName("test.part5.Initable3");
        System.out.println("After creating Initable3 ref");
        // 如果一个static字段不是final的,那么在对它访问时,总是要求在它被读取之前,
        // 首先进行链接(为这个字段分配存储空间)和初始化(初始化该存储空间)
        System.out.println(Initable3.staticNonFinal);
    }
}

class Initable {
    static final int STATIC_FINAL = 47;
    static final int STATIC_FINAL2 = ClassInitialization.rand.nextInt(1000);

    static {
        System.out.println("Initializing Initable");
    }
}

class Initable2 {
    static int staticNonFinal = 47;

    static {
        System.out.println("Initializing Initable2");
    }
}

class Initable3 {
    static int staticNonFinal = 74;

    static {
        System.out.println("Initializing Initable3");
    }
}
// 输出结果:
// After creating Initable ref
// 47
// Initializing Initable
// 258
// Initializing Initable2
// 47
// Initializing Initable3
// After creating Initable3 ref
// 74
19.2.2泛型的Class引用
public class GenericClassReferences {
    public static void main(String[] args) {
        Class intClass = int.class;
        Class<Integer> genericIntClass = int.class
        genericIntClass = Integer.class;    // 同一个东西
        intClass = double.class;
        // genericIntClass = double.class; // 非法
    }
}

通过使用泛型语法,可以让编译器强制执行额外的类型检查。

Class<Number> geenericNumberClass = int.class;

这看起来似乎是起作用的,因为Integer继承自Number。但事实却是不行,因为IntegerClass对象并不是NumberClass对象的子类。
为了创建一个限定指向某种类型或其子类的Class引用,需要将通配符与extends关键字配合使用,创建一个范围限定。

public class BoundedClassReferences {
    public static void main(String[] args) {
        Class<? extends Number> bounded = int.class;
        bounded = double.class;
        bounded = Number.class;
        // Or anything else derived from Number
    }
}

当将泛型语法用于Class对象时,newInstance()将返回该对象的确切类型。然而,这在某种程度上有些受限:

public class GenericToyTest {
    public static void main(String[] args) throws Exception {
        Class<FancyToy> ftClass = FancyToy.class;
        // Produces exact type
        FancyToy fancyToy = ftClass.newInstance();
        Class<? super FancyToy> up = ftClass.getSuperclass();
        // This won't compile
        // Class<Toy> up2 = ftClass.getSuperclass();
        // Only produces Object
        Object obj = up.newInstance();
    }
}

如果你手头的是超类,那编译器将只允许你声明超类引用为“某个类,它是FancyToy的超类”,就像在表达式 Class<? super FancyToy>中所看到的那样,而不会接收Class<Toy>这样的声明。这看上去显得有些怪,因为getSuperClass()方法返回的是基类(不是接口),并且编译器在编译期就知道它是什么类型了(在本例中就是Toy.class),而不仅仅只是“某个类”。不管怎样,正是由于这种含糊性,up.newInstance的返回值不是精确类型,而只是Object

19.3类型转换检测

已知的RTTI类型包括:

  1. 传统的类型转换,如(Shape),由RTTI确保转换的正确性,如果执行了一个错误的类型转换,就会抛出一个ClassCastException异常。
  2. 代表对象类型的Class对象。通过查询Class对象可以获取运行时所需的信息。
  3. 关键字instanceof返回一个布尔值,告诉对象是不是某个特定类型的实例。一般,可能想要查找某种类型,这时可以轻松地使用instanceof来度量所有对象。

在这里插入图片描述

// 每个Individual都有一个id()方法,如果没有为Individual提供名字,
// toString()方法只产生类名
public class Person extends Individual {
    public Person(String name) {
        super(name);
    }
}

public class Pet extends Individual {
    public Pet(String name) {
        super(name);
    }

    public Pet() {
        super();
    }
}

public class Dog extends Pet {
    public Dog(String name) {
        super(name);
    }

    public Dog() {
        super();
    }
}

// ...其他类和Dog大致一样,详见图

// 随机地创建不同类型的Pet,同事,还可以创建Pet数组和持有Pet的List,
// 为了使这个类更加普遍适用,将其定义为抽象类
public abstract class PetCreator implements Supplier<Pet> {
    private Random rand = new Random(47);

    // The List of the different types of Pet to create:
    public abstract List<Class<? extends Pet>> types();

    // Create one random Pet
    public Pet get() {
        int n = rand.nextInt(types().size());

        try {
            return types().get(n).newInstance();
        } catch (InstantiationException | IllegalAccessException e) {
            throw new RuntimeException(e);
        }

    }
}

// PetCreator的实现类
public class ForNameCreator extends PetCreator {
    // 需要随机生成的类型名:
    private static List<Class<? extends Pet>> types = new ArrayList<>(); 

    private static String[] typeNames = { "test.part12.Mutt", "test.part12.Pug", 
            "test.part12.EgyptianMau", "test.part12.Manx", "test.part12.Cymric", 
            "test.part12.Rat", "test.part12.Mouse", "test.part12.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();
    }

    @Override
    public List<Class<? extends Pet>> types() {
        return types;
    }
}

// 对Pet进行计数
public class PetCount {
    static class Counter extends HashMap<String, Integer> {
        public void count(String type) {
            Integer quantity = get(type);

            if (quantity == null) {
                put(type, 1);
            } else {
                put(type, quantity + 1);
            }
        }
    }

    public static void countPets(PetCreator creator) {
        Counter counter = new Counter();

        // List each individual pet:
        // Pets.array()产生随机Pet集合
        for (Pet pet : Pets.array(20)) {
            System.out.print(pet.getClass().getSimpleName() + " ");

            // instanceof有一个严格的限制:只可以将它与命名类型进行比较,
            // 而不能与Class对象作比较
            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 EgyptianMau) {
                counter.count("EgyptianMau");
            }

            if (pet instanceof Manx) {
                counter.count("Manx");
            }

            if (pet instanceof Cymric) {
                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");
            }
        }

        // Show the counts:
        System.out.println();
        System.out.println(counter);
    }

    public static void main(String[] args) {
        countPets(new ForNameCreator());
    }
}
19.3.1使用类字面量
// 如果使用类字面量重新实现PetCreator类的话,其结果在很多方面都会更清楚
public class LiteralPetCreator extends PetCreator {
    // try代码块不再需要,因为它是编译时计算的,不会引发任何异常
    public static final List<Class<? extends Pet>> ALL_TYPES = Collections.unmodifiableList(
            Arrays.asList(
                    Pet.class, Dog.class, Cat.class, Rodent.class, Mutt.class, 
                    Pug.class, EgyptianMau.class, Manx.class, Cymric.class, 
                    Rat.class, Mouse.class, Hamster.class
            )
    );  // 用于随机创建的类型:

    private static final List<Class<? extends Pet>> TYPES = ALL_TYPES.subList(
            ALL_TYPES.indexOf(Mutt.class), ALL_TYPES.size()
    );

    @Override
    public List<Class<? extends Pet>> types() {
        return TYPES;
    }

    public static void main(String[] args) {
        System.out.println(TYPES);
    }
}

public class Pets {
    // 创建一个使用LiteralPetCreator的外观模式
    public static final PetCreator CREATOR = new LiteralPetCreator();

    public static Pet get() {
        return CREATOR.get();
    }

    public static Pet[] array(int size) {
        Pet[] result = new Pet[size];

        for (int i = 0; i < size; i++) {
            result[i] = CREATOR.get();
        }

        return result;
    }

    public static List<Pet> list(int size) {
        List<Pet> result = new ArrayList<>();
        Collections.addAll(result, array(size));
        return result;
    }

    public static Stream<Pet> stream() {
        return Stream.generate(CREATOR);
    }
}

public class PetCount2 {
    public static void main(String[] args) {
        PetCount.countPets(Pets.CREATOR);
    }
}
19.3.2一个动态instanceof函数

Class.isInstance()方法提供了一种动态测试对象类型的方法。

public class PetCount3 {
    static class Counter extends LinkedHashMap<Class<? extends Pet>, Integer> {
        Counter() {
            super(
                    LiteralPetCreator.ALL_TYPES.stream()
                            .map(lpc -> Pair.make(lpc, 0))
                            .collect(Collectors.toMap(Pair::key, Pair::value))
            );
        }

        public void count(Pet pet) {
            // Class.isInstance()替换instanceof
            entrySet().stream()
                    .filter(pair -> pair.getKey().isInstance(pet))
                    .forEach(pair -> put(pair.getKey(), pair.getValue() + 1));
        }

        @Override
        public String toString() {
            String result = entrySet().stream()
                    .map(pair -> String.format("%s=%s", 
                            pair.getKey().getSimpleName(), 
                            pair.getValue())
                    )
                    .collect(Collectors.joining(", "));
            return "{" + result + "}";
        }
    }

    public static void main(String[] args) {
        Counter petCount = new Counter();
        Pets.stream()
                .limit(20)
                .peek(petCount::count)
                .forEach(p -> System.out.print(p.getClass().getSimpleName() + " "));
        System.out.println("n" + petCount);
    }
}
19.3.3递归计数
// PetCount3.Counter中的Map预先加载了所有不同的Pet类。
// 可以使用Class.isAssignableFrom()而不是预加载Map,
// 并创建一个不限于计数Pet的通用工具
public class TypeCounter extends HashMap<Class<?>, Integer> {
    private Class<?> baseType;

    public TypeCounter(Class<?> baseType) {
        this.baseType = baseType;
    }

    public void count(Object obj) {
        Class<?> type = obj.getClass();

        // 使用isAssignableFrom进行运行时检查,以验证传递的对象的类型是否是感兴趣的层次结构
        if (!baseType.isAssignableFrom(type)) {
            throw new RuntimeException(obj + " incorrect type: " + type + 
                    ", should be type or subtype of " + baseType);
        }

        countClass(type);
    }

    // 首先计算类的确切类型。然后,如果baseType可以从超类赋值,则在超类上递归调用countClass()
    private void countClass(Class<?> type) {
        Integer quantity = get(type);
        put(type, quantity == null ? 1 : quantity + 1);
        Class<?> superclass = type.getSuperclass();

        if (superclass != null && baseType.isAssignableFrom(superclass)) {
            countClass(superclass);
        }
    }

    @Override
    public String toString() {
        String result = entrySet().stream()
                .map(pair -> String.format("%s=%s",
                        pair.getKey().getSimpleName(),
                        pair.getValue())
                )
                .collect(Collectors.joining(", "));
        return "{" + result + "}";
    }
}

public class PetCount4 {
    public static void main(String[] args) {
        TypeCounter counter = new TypeCounter(Pet.class);
        Pets.stream()
                .limit(20)
                .peek(counter::count)
                .forEach(p -> System.out.print(p.getClass().getSimpleName() + " "));
        System.out.println("n" + counter);
    }
}
19.4注册工厂

Pet层次结构生成对象的问题是,每当向层次结构中添加一种新类型的Pet时,必须记住将其添加到LiteralPetCreator的条目中。在一个定期添加更多类的系统中,这可能会成为问题。
可能会考虑向每个子类添加静态初始值设定项,因此初始值设定项会将其类添加到某个列表中。不幸的是,静态初始值设定项仅在首次加载类时调用,因此存在鸡和蛋的问题:生成器的列表中没有类,因此它无法创建该类的对象,因此类不会被加载并放入列表中。
基本上,必须自己手动创建列表(除非编写了一个工具来搜索和分析源代码,然后创建和编译列表)。所以能做的最好的事情就是把列表集中放在一个明显的地方。层次结构的基类可能是最好的地方

// 使用工厂方法设计模式将对象的创建推迟到类本身。工厂方法可以以多态的方式调用,
// 并创建适当类型的对象。事实证明,Supplier用T get()描述了原型工厂方法,
// 协变返回类型允许get()为Supplier的每个子类实现返回不同的类型
public class RegisteredFactories {
    public static void main(String[] args) {
        Stream.generate(new Part())
                .limit(10)
                .forEach(System.out::println);
    }
}

class Part implements Supplier<Part> {
    static List<Supplier<? extends Part>> prototypes = Arrays.asList(
            new FuelFilter(), new AirFilter(), new CabinAirFilter(), new OilFilter(), new FanBelt(), new PowerSteeringBelt(),
            new GeneratorBelt());
    private static Random rand = new Random(47);

    @Override
    public String toString() {
        return getClass().getSimpleName();
    }

    // 并非层次结构中的所有类都应实例化,这里的Filter和Belt只是分类器,
    // 这样就只创建它们的子类
    @Override
    public Part get() {
        int n = rand.nextInt(prototypes.size());
        return prototypes.get(n).get();
    }
}

class Filter extends Part {
}

class FuelFilter extends Filter {
    @Override
    public Part get() {
        return new FuelFilter();
    }
}

class AirFilter extends Filter {
    @Override
    public Part get() {
        return new AirFilter();
    }
}

class CabinAirFilter extends Filter {
    @Override
    public Part get() {
        return new CabinAirFilter();
    }
}

class OilFilter extends Filter {
    @Override
    public Part get() {
        return new OilFilter();
    }
}

class Belt extends Part {
}

class FanBelt extends Belt {
    @Override
    public Part get() {
        return new FanBelt();
    }
}

class GeneratorBelt extends Belt {
    @Override
    public Part get() {
        return new GeneratorBelt();
    }
}

class PowerSteeringBelt extends Belt {
    @Override
    public Part get() {
        return new PowerSteeringBelt();
    }
}
19.6反射:运行时类信息

当使用反射与未知类型的对象交互时,JVM将查看该对象,并看到它属于特定的类(就像普通的RTTI)。在对其执行任何操作之前,必须加载Class对象。因此,该特定类型的.class文件必须在本地计算机上或通过网络对JVM仍然可用。因此,RTTI和反射的真正区别在于,使用RTTI时,编译器在编译时会打开并检查.class文件。换句话说,可以用正常的方式调用一个对象的所有方法。通过反射,.class文件在编译时不可用:它由运行时环境打开并检查。

19.7动态代理

代理是基本的设计模式之一。一个对象封装真实对象,代替其提供其他或不同的操作,这些操作通常涉及到与真实对象的通信,因此代理通常充当中间对象。

public class SimpleProxyDemo {
    public static void consumer(Interface iface) {
        iface.doSomething();
        iface.somethingElse("bonobo");
    }

    public static void main(String[] args) {
        consumer(new RealObject());
        consumer(new SimpleProxy(new RealObject()));
    }
}

interface Interface {
    void doSomething();

    void somethingElse(String arg);
}

class RealObject implements Interface {
    @Override
    public void doSomething() {
        System.out.println("doSomething");
    }

    @Override
    public void somethingElse(String arg) {
        System.out.println("somethingElse " + arg);
    }
}

class SimpleProxy implements Interface {
    private Interface proxied;

    SimpleProxy(Interface proxied) {
        this.proxied = proxied;
    }

    @Override
    public void doSomething() {
        System.out.println("SimpleProxy doSomething");
        proxied.doSomething();
    }

    @Override
    public void somethingElse(String arg) {
        System.out.println("SimpleProxy somethingElse " + arg);
        proxied.somethingElse(arg);
    }
}

当希望将额外的操作与真实对象做分离时,代理可能会有所帮助,尤其是想要轻松地启用额外的操作时,反之亦然(设计模式就是封装变更,所以必须改变一些东西以证明模式的合理性)。
Java的动态代理更进一步,不仅动态创建代理对象而且动态处理对代理方法的调用。在动态代理上进行的所有调用都被重定向到单个调用处理程序,该处理程序负责发现调用的内容并决定如何处理。

public class SimpleDynamicProxy {
    public static void consumer(Interface iface) {
        iface.doSomething();
        iface.somethingElse("bonobo");
    }

    public static void main(String[] args) {
        RealObject real = new RealObject();
        consumer(real);

        // Insert a proxy and call again
        Interface proxy = (Interface) Proxy.newProxyInstance(
                Interface.class.getClassLoader(),
                new Class[] { Interface.class },
                new DynamicProxyHandler(real));
        consumer(proxy);
    }
}

class DynamicProxyHandler implements InvocationHandler {
    private Object proxied;

    DynamicProxyHandler(Object proxied) {
        this.proxied = proxied;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("**** proxy: " + proxy.getClass() + 
                ", method: " + method + ", args: " + args);

        if (args != null) {
            for (Object arg : args) {
                System.out.println(" " + arg);
            }
        }

        return method.invoke(proxied, args);
    }
}
19.8Optional

实际上,在所有地方都使用Optional是没有意义的,有时候检查一下是不是null也挺好的,或者有时可以合理地假设不会出现null,甚至有时候检查NullPointException也是可以接受的。Optional最有用武之地的是在那些更接近数据的地方,在问题空间中代表实体的对象上。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值