第十四章 类型信息2.0

[color=blue]类字面常量[/color]

Java 还提供了另一种方法来生成Class对象的引用:使用“类字面常量(class literal)”。对上述程序来说,看起来就象下面这样:

FancyToy.class;

这样做不仅更简单,而且更安全,因为它在编译期就会受到检查。并且它无需方法调用,所以也更高效。

类字面常量不仅可以应用于普通的类,也可以应用于接口、数组以及基本数据类型。以外,对于基本数据类型的包装类,还有一个标准域TYPE。TYPE域是一个引用,指向对应的基本数据类型的Class对象,如下所示:

boolean.class Boolean.TYPE 等

我建议使用”.class”的形式,以保持它们与常规类的一致性。

注意,有一点很有趣,当使用“.class”来创建对Class对象的引用时,不会自动地初始化该Class对象。为了使用类而作的准备工作实际包括三个步骤:

1. 加载,这是由类加载器执行的。该步骤将查找字节码,并从这些字节码中创建一个Class对象。

2. 链接。在链接阶段将验证类中的字节码,为静态域分配存储空间,并且如果必须的话,将解析这个类创建的对其他类的所有引用。

3. 初始化、如果该类具有超类,则对其初始化,执行静态初始化器和静态初始化快。

Class initable = Initable.class;
System.out.println(initable.staitcFinal);

初始化有效地实现了尽可能的“惰性”。从对initable引用的创建中可以看到,仅使用.class语法来获得对类的引用不会引发初始化。但是为了产生Class引用,Class.forName()立即就进行了初始化,就像在initable3引用的创建中所看到的。

如果一个static final值是“编译器常量”,就像Initable.staticFinal那样,那么这个值不需要对Initable类进行初始化就可以被读取。但是,如果只是将一个域设置为static和final的,还不足以确保这种行为,例如,对Initable.staticFinal2的访问将强制进行类的初始化,因为它不是一个编译器常量。

如果一个static域不是final的,那么在对它访问时,总是要求在它被读取之前,要先进行链接和初始化,就像在对Initable2.staticNonFinal的访问中所看到的那样。

[color=blue]泛化的Class引用[/color]

Class引用总是指向某个Class对象,它可以制造类的实例,并包含可作用于这些实例的所有方法代码。它还包含该类的静态成员,因此,Class引用表示的就是它所指向的对象的确切类型,而该对象便是Class类的一个对象。

下面这两种语法都是正确的:
public static void main(String[] args) {
Class intClass = int.class;
Class<Integer> genericIntClass = int.class;
genericIntClass = Integer.class;
intClass = double.class;
//genericIntClass = double.class //illegal
}

如果你想稍微放松点限制,应该怎么办呢?乍一看,好像可以这样:

Class<Number> genericNumberClass = int.class

这看起来似乎是其作用的,因为Integer继承自Number。[color=red]但是它无法工作[/color],因为Integer Class对象不是Number Class对象的子类。

为了能在泛化的Class引用时放松限制,我使用了通配符,它是java泛型的一部分。通配符就是“?”,表示“任何事物”。因此,我们可以在上例的普通Class引用中添加通配符,并产生相同的结果:

public static void main(String[] args) {
Class<?> intClass = int.class;
intClass = double.class;
}

在java SE5中,Class<?>优于平凡的Class,即便它们是等价的,它表示你不是疏忽才这么做的。

为了创建一个Class引用,它被限定为某种类型,或该类型的任何子类型,你需要将通配符与extends关键字相结合,创建一个范围。因此,与仅仅声明Class<Number>不同们现在做如下声明:

public static void main(String[] args) {
Class<? extends Number> dounded = int.class;
dounded = double.class;
dounded = Number.class;
}

当你将泛型语法用于Class对象时,会发生一件很有趣的事情:newInstance()将返回该对象的确切类型,而不仅仅只是在ToyTest.java中看到的基本的Object。这在某种程度上有些限制:

public static void main(String[] args) {
Class<FancyToy> ftClass = FancyToy.class;
FancyToy fancyToy = ftClass.newInstance();
Class<? super FancyToy> up = ftClass.getSuperclass();
//this wont compile
//Class<Toy> up2 = ftClass.getSuperclass();
//Only produces Object;
Object obj = up.newInstance();
}

超类只能这样:Class<? super FancyToy> up = ftClass.getSuperclass();
不能:Class<Toy> up2 = ftClass.getSuperclass();
由于这种含糊性:up.newInstance()的返回值不是精确的类型,而是Object:
Object obj = up.newInstance();

[color=blue]新的转型语法[/color]
public static void main(String[] args) {
Building b = new House();
Class<House> houseType = House.class;
House h = houseType.cast(b);
h = (House)b;
}

Case()方法接受参数对象,并将其转化为Class引用的类型。新的转型语法对于无法使用普通转型的情况显得非常有用。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值