logo资料库

Java泛型三篇文章,让你彻底理解泛型(super ,extend等区别).docx

第1页 / 共31页
第2页 / 共31页
第3页 / 共31页
第4页 / 共31页
第5页 / 共31页
第6页 / 共31页
第7页 / 共31页
第8页 / 共31页
资料共31页,剩余部分请下载后查看
Java 泛型
关键字说明
extends 示例
super 示例
小结
Java泛型简明教程
Java泛型由来的动机
泛型的构成
泛型类和接口
泛型方法和构造器(Constructor)
例子
Java 泛型通配符 extends 与 super Java 泛型 关键字说明    ? 通配符类型 表示类型的上界,表示参数化类型的可能是 T 或是 T 的子类 表示类型下界(Java Core 中叫超类型限定),表示参数化类型是此类型 的超类型(父类型),直至 Object extends 示例 static class Food{} static class Fruit extends Food{} static class Apple extends Fruit{} static class RedApple extends Apple{} List flist = new ArrayList(); // complie error: // flist.add(new Apple()); // flist.add(new Fruit()); // flist.add(new Object()); flist.add(null); // only work for null List 表示 “具有任何从 Fruit 继承类型的列表”,编译器无法确定 List 所持 有的类型,所以无法安全的向其中添加对象。可以添加 null,因为 null 可以表示任何类型。 所以 List 的 add 方法不能添加任何有意义的元素,但是可以接受现有的子类型 List 赋值。 Fruit fruit = flist.get(0);
Apple apple = (Apple)flist.get(0); 由于,其中放置是从 Fruit 中继承的类型,所以可以安全地取出 Fruit 类型。 flist.contains(new Fruit()); flist.contains(new Apple()); 在使用 Collection 中的 contains 方法时,接受 Object 参数类型,可以不涉及任何通配符, 编译器也允许这么调用。 super 示例 List flist = new ArrayList(); flist.add(new Fruit()); flist.add(new Apple()); flist.add(new RedApple()); // compile error: List flist = new ArrayList(); List 表示“具有任何 Fruit 超类型的列表”,列表的类型至少是一个 Fruit 类型, 因此可以安全的向其中添加 Fruit 及其子类型。由于 List中的类型可能是任 何 Fruit 的超类型,无法赋值为 Fruit 的子类型 Apple 的 List. // compile error: Fruit item = flist.get(0); 因为,List中的类型可能是任何 Fruit 的超类型,所以编译器无法确定 get 返回的对象类型是 Fruit,还是 Fruit 的父类 Food 或 Object. 小结 extends 可用于的返回类型限定,不能用于参数类型限定。 super 可用于参数类型限定,不能用于返回类型限定。 >带有 super 超类型限定的通配符可以向泛型对易用写入,带有 extends 子类型限定的通 配符可以向泛型对象读取
Java 泛型简明教程 泛型是 Java SE 5.0 中引入的一项特征,自从这项语言特征出现多年来,我相信, 几乎所有的 Java 程序员不仅听说过,而且使用过它。关于 Java 泛型的教程,免 费的,不免费的,有很多。我遇到的最好的教材有:  The Java Tutorial  Java Generics and Collections, by Maurice Naftalin and Philip Wadler  Effective Java 中文版(第 2 版), by Joshua Bloch. 尽管有这么多丰富的资料,有时我感觉,有很多的程序员仍然不太明白 Java 泛 型的功用和意义。这就是为什么我想使用一种最简单的形式来总结一下程序员需 要知道的关于 Java 泛型的最基本的知识。 Java 泛型由来的动机 理解 Java 泛型最简单的方法是把它看成一种便捷语法,能节省你某些 Java 类型 转换(casting)上的操作: 1 2 List box = ...; Apple apple = box.get(0); 上面的代码自身已表达的很清楚:box 是一个装有 Apple 对象的 List。get 方法返 回一个 Apple 对象实例,这个过程不需要进行类型转换。没有泛型,上面的代码 需要写成这样:
1 2 List box = ...; Apple apple = (Apple) box.get(0); 很明显,泛型的主要好处就是让编译器保留参数的类型信息,执行类型检查,执 行类型转换操作:编译器保证了这些类型转换的绝对无误。 相对于依赖程序员来记住对象类型、执行类型转换——这会导致程序运行时的失 败,很难调试和解决,而编译器能够帮助程序员在编译时强制进行大量的类型检 查,发现其中的错误。 泛型的构成 由泛型的构成引出了一个类型变量的概念。根据 Java 语言规范,类型变量是一 种没有限制的标志符,产生于以下几种情况:  泛型类声明  泛型接口声明  泛型方法声明  泛型构造器(constructor)声明 泛型类和接口
如果一个类或接口上有一个或多个类型变量,那它就是泛型。类型变量由尖括号 界定,放在类或接口名的后面: 1 2 3 public interface List extends Collection { ... } 简单的说,类型变量扮演的角色就如同一个参数,它提供给编译器用来类型检查 的信息。 Java 类库里的很多类,例如整个 Collection 框架都做了泛型化的修改。例如,我 们在上面的第一段代码里用到的 List 接口就是一个泛型类。在那段代码里,box 是一个 List对象,它是一个带有一个 Apple 类型变量的 List 接口的类实 现的实例。编译器使用这个类型变量参数在 get 方法被调用、返回一个 Apple 对 象时自动对其进行类型转换。 实际上,这新出现的泛型标记,或者说这个 List 接口里的 get 方法是这样的: 1 T get(int index); get 方法实际返回的是一个类型为 T 的对象,T 是在 List声明中的类型变量。 泛型方法和构造器(Constructor)
非常的相似,如果方法和构造器上声明了一个或多个类型变量,它们也可以泛型 化。 1 public static T getFirst(List list) 这个方法将会接受一个 List类型的参数,返回一个 T 类型的对象。 例子 你既可以使用 Java 类库里提供的泛型类,也可以使用自己的泛型类。 类型安全的写入数据… 下面的这段代码是个例子,我们创建了一个 List实例,然后装入一些数 据: 1 2 3 List str = new ArrayList(); str.add("Hello "); str.add("World."); 如果我们试图在 List装入另外一种对象,编译器就会提示错误: 1 str.add(1); // 不能编译 类型安全的读取数据… 当我们在使用 List对象时,它总能保证我们得到的是一个 String 对象:
1 String myString = str.get(0); 遍历 类库中的很多类,诸如 Iterator,功能都有所增强,被泛型化。List接口 里的 iterator()方法现在返回的是 Iterator,由它的 T next()方法返回的对象不 需要再进行类型转换,你直接得到正确的类型。 1 2 3 4 for (Iterator iter = str.iterator(); iter.hasNext();) { String s = iter.next(); System.out.print(s); } 使用 foreach “for each”语法同样受益于泛型。前面的代码可以写出这样: for (String s: str) { System.out.print(s); 1 2 3 } 这样既容易阅读也容易维护。 自动封装(Autoboxing)和自动拆封(Autounboxing)
在使用 Java 泛型时,autoboxing/autounboxing 这两个特征会被自动的用到,就像 下面的这段代码: 1 2 3 4 5 6 7 8 List ints = new ArrayList(); ints.add(0); ints.add(1); int sum = 0; for (int i : ints) { sum += i; } 然而,你要明白的一点是,封装和解封会带来性能上的损失,所有,通用要谨慎 的使用。 子类型 在 Java 中,跟其它具有面向对象类型的语言一样,类型的层级可以被设计成这 样:
分享到:
收藏