[关闭]
@liayun 2016-06-24T18:58:08.000000Z 字数 9916 阅读 1782

泛型

java基础加强


大家可以看看JDK帮助文档中的Class的定义,其中好多地方涉及到了<>的语法形式,这就是我们今天要讲解的泛型。
泛型是JDK1.5的所有新特性中最难深入掌握的部分,不过,我们在实际应用中不能掌握得那么深入,掌握泛型中一些最基本的内容就差不多了。

体验泛型

没有使用泛型时,只要是对象,不管是什么类型的对象,都可以存储进同一个集合中。使用泛型集合,可以将一个集合中的元素限定为一个特定类型,集合中只能存储同一个类型的对象,这样更安全;并且当从集合获取一个对象时,编译器也可以知道这个对象的类型,不需要对对象进行强制类型转换,这样更方便。

了解泛型

思考题:下面的代码会报错误吗?

  1. Vector v1 = new Vector<String>();
  2. Vector<Object> v = v1;

解:不会。因为原始类型可以引用一个参数化类型的对象,所以第一句没有错误;参数化类型可以引用一个原始类型的对象,所以第二句也没有错误。但不能将两句结合起来看,如Vector<Object> v = new Vector<String>();,那么就是错误的。

泛型中的?通配符

泛型中的?通配符的扩展

泛型集合类的综合案例

定义泛型方法

Java中的泛型类型(或者泛型)类似于C++中的模板,但是这种相似性仅限于表面,Java语言中的泛型基本上完全是在编译器中实现,用于编译器执行类型检查和类型推断,然后生成普通的非泛型的字节码,这种实现技术称为擦除(erasure)(编译器使用泛型类型信息保证类型安全,然后在生成字节码之前将其清除)。这是因为扩展虚拟机指令集来支持泛型被认为是无法接受的,这会为Java厂商升级其JVM造成难以逾越的障碍。所以,java的泛型采用了可以完全在编译器中实现的擦除方法。
例如,下面这两个方法,编译器会报告错误,它不认为是两个不同的参数类型,而认为是同一种参数类型:

  1. public static void applyVector(Vector<Date> v1) {
  2. }
  3. /*
  4. 不是重载,因为去类型化了
  5. public static void applyVector(Vector<Integer> v1) {
  6. }
  7. */

泛型方法的练习题

练习1,编写一个泛型方法,自动将Object类型的对象转换成其他类型。
解:

  1. private static <T> T autoConvert(Object obj) {
  2. return (T)obj;
  3. }

测试代码:

  1. Object obj = "abc";
  2. String x3 = autoConvert(obj);

泛型方法的另外一个常见应用就是调用者无需对返回值进行类型转换。
练习2,定义一个方法,可以将任意类型的数组中的所有元素填充为相应类型的某个对象。
解:

  1. private static <T> void fillArray(T[] a, T obj) {
  2. for(int i = 0; i < a.length; i++) {
  3. a[i] = obj;
  4. }
  5. }

练习3,采用自定泛型方法的方式打印出任意参数化类型的集合中的所有内容。
解:
分析:在这种情况下,前面的通配符方案要比范型方法更有效,当一个类型变量用来表达两个参数之间或者参数和返回值之间的关系时,即同一个类型变量在方法签名的两处被使用,或者类型变量在方法体代码中也被使用而不是仅在签名的时候使用,才需要使用范型方法
使用通配符:

  1. public static void printCollection(Collection<?> collection) {
  2. System.out.println(collection.size());
  3. for(Object obj : collection) {
  4. System.out.println(obj);
  5. }
  6. }

使用泛型方法:

  1. public static <E> void printCollection(Collection<E> cols) {
  2. for(E obj:cols) {
  3. System.out.println(obj);
  4. }
  5. }

练习4,定义一个方法,把任意参数类型的集合中的数据安全地复制到相应类型的数组中。
解:
分析:如果使用如下形式:static void copy(Collection a, Object[] b);有可能出现A类型的数据复制进B类型的数组中的情况。所以需要使用泛型方法进行定义。具体代码省略。

  1. public static <T> void copy1(Collection<T> dest, T[] src) {
  2. }

练习5,定义一个方法,把任意参数类型的一个数组中的数据安全地复制到相应类型的另一个数组中。
解:具体代码省略。

  1. public static <T> void copy2(T[] dest, T[] src) {
  2. }

练习6,编写一个泛形方法,接收一个任意数组,并颠倒数组中的所有元素。
解:

  1. public <T> void reverse(T[] arr) {
  2. int start = 0;
  3. int end = arr.length - 1;
  4. while(true) {
  5. // 如果头尾两个指针碰到一块,就不要交换了
  6. if(start >= end)
  7. break;
  8. T temp = arr[start];
  9. arr[start] = arr[end];
  10. arr[end] = temp;
  11. start++;
  12. end--;
  13. }
  14. }

类型参数的类型推断

编译器判断范型方法的实际类型参数的过程称为类型推断,类型推断是相对于知觉推断(?,因为直觉推断吧!)的,其实现方法是一种非常复杂的过程。
根据调用泛型方法时实际传递的参数类型或返回值的类型来推断,具体规则如下:

定义泛型类型

注意:

问题:类中多个方法需要使用泛型,是使用类级别的泛型,还是使用方法级别的泛型?
答:使用类级别的泛型。

通过反射获得泛型的参数化类型

示例代码:

  1. public class GenericTest {
  2. public static void main(String[] args) {
  3. Method applyMethod = GenericTest.class.getMethod("applyVector", Vector.class);
  4. Type[] types = applyMethod.getGenericParameterTypes();
  5. ParameterizedType pType = (ParameterizedType)types[0];
  6. System.out.println(pType.getRawType()); // class java.util.Vector
  7. System.out.println(pType.getActualTypeArguments()[0]); // class java.util.Date
  8. }
  9. public static void applyVector(Vector<Date> v1) {
  10. }
  11. }

留待以后细研。

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注