1.为什么要使用泛型(generic)程序设计?
在《Java核心技术》中的阐述:编写的代码可以被许多不同类型的对象所重用,这样就允许泛型代码和遗留代码之间能够相互操作。
提到的两个概念:类型参数、通配符类型
在《Head first Java》中的解释似乎更通俗易懂些:几乎所有以泛型编写的程序都与处理集合有关,虽然泛型可以用在其他地方,但它主要目的还是让你能够写成有类型安全性的集合,也就是说编译器能够帮忙防止你把Dog加到一群Cat中。
就像在Collection类的sort方法中有写明:public static <T extends Comparable<?super T>>void sort (List<T> list)其中Comparable<?super T>就是在对参数类型做限制,加入的是什么对象,返回的还是该对象的引用。
在泛型功能出现前,编译器无法注意到你加入集合中的东西是什么,因为所有的集合都写成处理Object类型,可以把任何东西放进ArrayList中,有点像ArrayList<Object>
在Java的程序或文件中只要用到<>这组符号, 就表示泛型正在作用—它是一种从Java 5.0开始加入的特质。
ArrayList虽然是最常用的,但偶尔还是会有特殊情况,比如ArrayList没有排序方法。下面列出几个重要的:
LinkedList:针对经常插入或删除中间元素所设计的高效率集合(实际上ArrayList还是比较实用)
TreeSet:以有序状态保持并防止重复
(TreeSet的元素必须是Comparable
使用TreeSet,下列其中一项必须为真
集合中的元素必须是有实现Comparable的类型
使用重载,取用Comparator参数的构造函数来创建TreeSet
)
HashSet:防止重复的集合,可快速地找到相符的元素。(但是要override hashcode()和equals()这两个方法)
HashMap:可用成对的name/value来保存于取出
LinkedHashMap:类似HashMap,但是可记住元素插入的顺序,也可以设定成按照元素上次存取的先后来排序。
关于泛型
1) 创建被泛型化类(例如ArrayList)的实例
创建ArrayList时你必须要指定它所容许的对象,就像单纯的数组那样。
new ArrayList<Song>()
2) 声明与指定泛型类型的变量
多态遇到泛型类型会怎么样?
数组与ArrayList
对于ArrayList,如果把方法声明成取用ArrayList<Animal>,它就智慧取用ArrayList< Animal >参数,ArrayList< Animal的子类>就行,而数组就可以实现。
数组的类型是在运行期间检查的,但集合的类型检查只会发生在编译期间,所以如果出现类型错误,对于集合来说,编译是都无法通过的。
泛型与多态
万用字符?:用来接受Animal子型参数的方法
public void takeAnimals (ArrayList<? extends Animal> animals){ ……
}
extends同时代表继承和实现
List<Song> songlist=new ArrayList<Song>()
1) 声明(与调用)取用泛型类型的方法
声明:void foo(List<Song> list)
调用x.foo(songList)
总结:主要就是泛型类,泛型变量,泛型方法。
注:API的设计团队已经涵盖了大部分你会遇到的额数据结构,而几乎只有集合会真的需要泛型。
使用泛型的类
ArrayList是最常用的泛型化类型,我们从查看它的说明文件说起。
public class ArrayList<E> extends Abstraction<E> implements List<E>…{
……
}
其中E就被称为类型参数
运用泛型的方法
泛型的类代表类的声明用到类型参数,泛型的方法代表方法的声明特征用到类型参数
在方法中的类型参数有几种不同的运用方式:
1) 使用定义在类声明的类型参数
public class ArrayList<E> extends AbstracList<E>…{
public boolean add(E o)
2) 使用未定义在类声明的类型参数
public <T extends Animal> void takeThing(ArrayList<T> list)
代表传入Animal或其子型来出初始化ArrayList。
从Collection的API说明文件中我们发现3个主要的接口:List、Set和Map
LIST:对付顺序的好帮手
是一种知道索引位置的集合,List知道某物在系列集合中的位置,可以有多个元素引用相同的对象。
SET:注重独一无二的性质
不允许重复的集合。它知道某物是否已经存在于集合中,不会有多个元素引用相同的对象
MAP:用key来搜索的专家
使用成对的键值和数据值。Map会维护与key有关联的值,两个key可以引用相同的对象,但是key不能重复,典型的key是String,但也可以是任何对象。
集合的API
以上内容来自Head first Java,接下来的内容from 《Java核心技术》 (感觉该书阐述的有点抽象)
泛型程序设计分为3个能力级别:
1) 仅仅使用泛型类
2) 能够利用Java泛型系统地解决代码衔接,泛型类混合问题。
3) 实现自己的泛型类和泛型方法。
2.泛型类:具有一个或多个类型变量的类。
pair类引入了一个类型变量T,用尖括号<>括起来,并放在类名后面。
泛型类可以有多个类型变量,例如,也可以这样定义pair类,其中第一个域和第二个域使用不同的类型:
public class Pair<T,U>{. . .}
3.泛型方法:
如下为定义一个带有类型参数的简单方法:
class ArrayAlg
{
public static <T> T getMiddle(T. . .a)
{
return a[a.length/2];
}
}
类型变量放在修饰符public static的后面,返回类型的前面。
泛型方法可以定义在普通类中,也可以定义在泛型类中。
4.类型变量的限定
为类型变量实现接口,来达到限定的目的,避免产生编译错误,如,为T实现Comparable接口
public static <T extends Comparable> T getMiddle(T. . .a)
类型擦除:去掉类型变量的限制,或者将类型变量替换为object
桥方法:为解决类型擦除与多态发生冲突的问题
有关Java泛型转换的事实:
虚拟机没有泛型,只有普通的类和方法。
所有的参数类型都用它们的限定类型替换
桥方法被合成来保持多态
为保持类型安全性,必要时插入强制类型转换