Class<T>等泛型

Class<T>

类 Class 已经泛型化了,但是很多人一开始都感觉其泛型化的方式很混乱。Class<T> 中类型参数 T 的含义是什么?事实证明它是所引用的类接口。怎么会是这样的呢?那是一个循环推理?如果不是的话,为什么这样定义它?

在以前的 JDK 中,Class.newInstance() 方法的定义返回 Object,您很可能要将该返回类型强制转换为另一种类型:

class Class { 
Object newInstance();
}

但是使用泛型,您定义 Class.newInstance() 方法具有一个更加特定的返回类型:

class Class<T> { 
T newInstance();
}

如何创建一个 Class<T> 类型的实例?就像使用非泛型代码一样,有两种方式:调用方法 Class.forName() 或者使用类常量 X.class。Class.forName() 被定义为返回 Class<?>。另一方面,类常量 X.class 被定义为具有类型 Class<X>,所以 String.class 是 Class<String> 类型的。

让 Foo.class 是 Class<Foo> 类型的有什么好处?大的好处是,通过类型推理的魔力,可以提高使用反射的代码的类型安全。另外,还不需要将 Foo.class.newInstance() 强制类型转换为 Foo。

考虑一个方法,它从数据库检索一组对象,并返回 JavaBeans 对象的一个集合。您通过反射来实例化和初始化创建的对象,但是这并不意味着类型安全必须完全被抛至脑后。考虑下面这个方法:

public static<T> List<T> getRecords(Class<T> c, Selector s) {
// Use Selector to select rows
List<T> list = new ArrayList<T>();
for (/* iterate over results */) {
    T row = c.newInstance();
    // use reflection to set fields from result
    list.add(row); 
}
return list;
}

可以像下面这样简单地调用该方法:

List<FooRecord> l = getRecords(FooRecord.class, fooSelector);

编译器将会根据 FooRecord.class 是 Class<FooRecord> 类型的这一事实,推断 getRecords() 的返回类型。您使用类常量来构造新的实例并提供编译器在类型检查中要用到的类型信息。

 

用 Class<T> 替换 T[]

Collection 接口包含一个方法,用于将集合的内容复制到一个调用者指定类型的数组中:

public Object[] toArray(Object[] prototypeArray) { ... }

toArray(Object[]) 的语义是,如果传递的数组足够大,就会使用它来保存结果,否则,就会使用反射分配一个相同类型的新数组。一般来说,单独传递一个数组作为参数来提供想要的返回类型是一个小技巧,但是在引入泛型之前,这是与方法交流类型信息最方便的方式。

有了泛型,就可以用一种更加直观的方式来做这件事。不像上面这样定义 toArray(),泛型 toArray() 可能看起来像下面这样:

public<T> T[] toArray(Class<T> returnType)

调用这样一个 toArray() 方法很简单:

FooBar[] fba = something.toArray(FooBar.class);

Collection 接口还没有改变为使用该技术,因为这会破坏许多现有的集合实现。但是如果使用泛型从新构建 Collection,则当然会使用该方言来指定它想要返回值是哪种类型。

 

Enum<E>

JDK 5.0 中 Java 语言另一个增加的特性是枚举。当您使用 enum 关键字声明一个枚举时,编译器就会在内部为您生成一个类,用于扩展 Enum 并为枚举的每个值声明静态实例。所以如果您说:

public enum Suit {HEART, DIAMOND, CLUB, SPADE};

编译器就会在内部生成一个叫做 Suit 的类,该类扩展 java.lang.Enum<Suit> 并具有叫做 HEART、DIAMOND、CLUB 和 SPADE 的常量(public static final)成员,每个成员都是 Suit 类。

与 Class 一样,Enum 也是一个泛型类。但是与 Class 不同,它的签名稍微更复杂一些:

class Enum<E extends Enum<E>> { . . . }

这究竟是什么意思?这难道不会导致无限递归?

我们逐步来分析。类型参数 E 用于 Enum 的各种方法中,比如 compareTo() 或 getDeclaringClass()。为了这些方法的类型安全,Enum 类必须在枚举的类上泛型化。

所以 extends Enum<E> 部分如何理解?该部分又具有两个部分。第一部分指出,作为 Enum 的类型参数的类本身必须是 Enum 的子类型,所以您不能声明一个类 X 扩展 Enum<Integer>。第二部分指出,任何扩展 Enum 的类必须传递它本身 作为类型参数。您不能声明 X 扩展 Enum<Y>,即使 Y 扩展 Enum。

总之,Enum 是一个参数化的类型,只可以为它的子类型实例化,并且这些子类型然后将根据子类型来继承方法。幸运的是,在 Enum 情况下,编译器为您做这些工作,一切都很好。

 

与非泛型代码相互操作

数百万行现有代码使用已经泛型化的 Java 类库中的类,比如集合框架、Class 和 ThreadLocal。JDK 5.0 中的改进不要破坏所有这些代码是很重要的,所以编译器允许您在不指定其类型参数的情况下使用泛型类。

当然,以“旧方式”做事没有新方式安全,因为忽略了编译器准备提供的类型安全。如果您试图将 List<String> 传递给一个接受 List 的方法,它将能够工作,但是编译器将会发出一个可能丧失类型安全的警告,即所谓的“unchecked conversion(不检查转换)”警告。

没有类型参数的泛型,比如声明为 List 类型而不是 List<Something> 类型的变量,叫做原始类型。原始类型与参数化类型的任何实例化是赋值兼容的,但是这样的赋值会生成 unchecked-conversion 警告。

为了消除一些 unchecked-conversion 警告,假设您不准备泛型化所有的代码,您可以使用通配符类型参数。使用 List<?> 而不使用 List。List 是原始类型;List<?> 是具有未知类型参数的泛型。编译器将以不同的方式对待它们,并很可能发出更少的警告。

无论在哪种情况下,编译器在生成字节码时都会生成强制类型转换,所以生成的字节码在每种情况下都不会比没有泛型时更不安全。如果您设法通过使用原始类型或类文件来破坏类型安全,就会得到与不使用泛型时得到的相同的 ClassCastException 或 ArrayStoreException。


已检查集合

作为从原始集合类型迁移到泛型集合类型的帮助,集合框架添加了一些新的集合包装器,以便为一些类型安全 bug 提供早期警告。就像 Collections.unmodifiableSet() 工厂方法用一个不允许任何修改的 Set 包装一个现有 Set 一样,Collections.checkedSet()(以及 checkedList() 和 checkedMap())工厂方法创建一个包装器(或者视图)类,以防止您将错误类型的变量放在集合中。

checkedXxx() 方法都接受一个类常量作为参数,所以它们可以(在运行时)检查这些修改是允许的。典型的实现可能像下面这样:


public class Collections { 
public static <E> Collection<E> checkedCollection(Collection<E> c, Class<E> type ) { 
    return new CheckedCollection<E>(c, type); 
}

private static class CheckedCollection<E> implements Collection<E> { 
    private final Collection<E> c; 
    private final Class<E> type;

    CheckedCollection(Collection<E> c, Class<E> type) { 
      this.c = c; 
      this.type = type; 
    }

    public boolean add(E o) { 
      if (!type.isInstance(o)) 
        throw new ClassCastException(); 
      else
        return c.add(o); 
    } 

转载自http://www.cnblogs.com/panjun-Donet/archive/2008/09/27/1300609.html

2

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值