Java泛型详解
来源:互联网 发布:魔兽美工代码编辑器 编辑:程序博客网 时间:2024/06/06 07:31
Java泛型(generics)是JDK 5中引入的一个新特性,允许在定义类和接口的时候使用类型参数(type parameter)。声明的类型参数在使用时用具体的类型来替换。泛型最主要的应用是在JDK 5中的新集合类框架中。对于泛型概念的引入,开发社区的观点是褒贬不一。从好的方面来说,泛型的引入可以解决之前的集合类框架在使用过程中通常会出现的运行时刻类型错误(安全性更好) ,因为编译器可以在编译时刻就发现很多明显的错误。而从不好的地方来说,为了保证与旧有版本的兼容性,Java泛型的实现上存在着一些不够优雅的地方。当然这也是任何有历史的编程语言所需要承担的历史包袱。后续的版本更新会为早期的设计缺陷所累。
类型擦除
正确理解泛型概念的首要前提是理解类型擦除(type erasure)。 Java中的泛型基本上都是在编译器这个层次来实现的。在生成的很多泛型的奇怪特性都与这个类型擦除的存在有关,包括:
- 泛型类并没有自己独有的Class类对象。
比如并不存在List<String>. class或是List<Integer>.class, 而只有List.class。 - 静态变量是被泛型类的所有实例所共享的。
对于声明为MyClass<T>的类, 访问其中的静态变量的方法仍然是 MyClass.myStaticVar。不管是通过new MyClass<String>还是new MyClass<Integer>创建的对象, 都是共享一个静态变量。 - 泛型的类型参数不能用在Java异常处理的catch语句中。
因为异常处理是由JVM在运行时刻来进行的。 由于类型信息被擦除, JVM是无法区分两个异常类型MyException< String>和MyException<Integer>的。 对于JVM来说,它们都是 MyException类型的。 也就无法执行与异常对应的catch语句。
类型擦除的基本过程也比较简单,
class MyString implements Comparable<String> { public int compareTo(String str) { return 0; } }
当类型信息被擦除之后,上述类的声明变成了class MyString implements Comparable。但是这样的话,
实例分析
了解了类型擦除机制之后,
public void inspect(List<Object> list) { for (Object obj : list) { System.out.println(obj); } list.add(1); //这个操作在当前方法的上下文是合法的。 } public void test() { List<String> strs = new ArrayList<String>(); inspect(strs); //编译错误 }
这段代码中,inspect方法接受List<Object>
通配符(?)与上下界(extends ,super )
在使用泛型类的时候,既可以指定一个具体的类型,如List<
如上所示,试图对一个带通配符的泛型类进行操作的时候,
因为对于List<?>中的元素只能用Object来引用,
类型系统
在Java中,引入泛型之后的类型系统增加了两个维度:
- 相同类型参数的泛型类的关系取决于泛型类自身的继承体系结构。
即List<String>是Collection< String> 的子类型,List<String> 可以替换Collection<String>。 这种情况也适用于带有上下界的类型声明。 - 当泛型类的类型声明中使用了通配符的时候, 其子类型可以在两个维度上分别展开。如对Collection<
? extends Number>来说, 其子类型可以在Collection这个维度上展开, 即List<? extends Number>和Set<? extends Number>等;也可以在Number这个层次上展开, 即Collection<Double>和 Collection<Integer>等。如此循环下去, ArrayList<Long>和 HashSet<Double> 等也都算是Collection<? extends Number>的子类型。 - 如果泛型类中包含多个类型参数,
则对于每个类型参数分别应用上面的规则。
开发自己的泛型类
泛型类与一般的Java类基本相同,只是在类和接口定义上多出来
class ClassTest<X extends Number, Y, Z> { private X x; private static Y y; //编译错误,不能用在静态变量中 public X getFirst() { //正确用法 return x; } public void wrong() { Z z = new Z(); //编译错误,不能创建对象 ,因为泛型类并没有自己独立的Class类对象 } }
最佳实践
在使用泛型的时候可以遵循一些基本的原则,- 在代码中避免泛型类和原始类型的混用。比如List<
String>和List不应该共同使用。 这样会产生一些编译器警告和潜在的运行时异常。当需要利用JDK 5之前开发的遗留代码,而不得不这么做时, 也尽可能的隔离相关的代码。 - 在使用带通配符的泛型类的时候,
需要明确通配符所代表的一组类型的概念 。由于具体的类型是未知的,很多操作是不允许的。 - 泛型类最好不要同数组一块使用。你只能创建new List<?>[10]这样的数组,无法创建new List<String>[10]这样的。
这限制了数组的使用能力,而且会带来很多费解的问题。因此, 当需要类似数组的功能时候,使用集合类即可。 - 不要忽视编译器给出的警告信息。
- Java泛型详解
- java泛型详解
- java泛型详解
- Java泛型详解
- Java泛型详解
- java泛型详解
- Java泛型详解
- java泛型详解
- java 泛型详解
- java 泛型详解
- Java 泛型详解
- java 泛型详解
- java 泛型详解
- java泛型详解
- java 泛型详解
- Java泛型详解
- java 泛型详解
- java 泛型详解
- BlockingQueue阻塞队列和生产者-消费者模式
- Oracle数据类型之number
- java中直接调用groovy的类.
- 源码网站
- 认为认为
- Java泛型详解
- 推广的一个方法
- win8下 ExecJS::RuntimeError in **#**错误的解决办法
- 归并排序
- 高级数据结构-SkipList
- HDU 1050 解题报告
- javasript正则表达式
- Spring Data JPA 复杂/多条件组合查询
- Android开发学习笔记:浅谈WebView