泛型(Generic)
JDK5以前,对象保存到集合中就会失去其特性,取出时通常要程序员手工进行类型的强制转换,这样不可避免就会引发程序的一些安全性问题。例如:
ArrayList list = new ArrayList();
list.add("abc");
Integer num = (Integer) list.get(0); //运行时会出错,但编码时发现不了
使用泛型后的代码
ArrayList<String> list = new ArrayList<String>();
list.add(“abc”);
Integer num = list.get(0);//写程序时则报错
JDK5中的泛形允许程序员在编写集合代码时,就限制集合的处理类型,从而把原来程序运行时可能发生问题,转变为编译时的问题,以此提高程序的可读性和稳定性(尤其在大型程序中更为突出)。
注意:泛型是提供给javac编译器使用的,它用于限定集合的输入类型,让编译器在源代码级别上,即挡住向集合中插入非法数据。但编译器编译完带有泛形的java程序后,生成的class文件中将不再带有泛形信息,以此使程序运行效率不受到影响,这个过程称之为“擦除”。
泛形的基本术语:
以ArrayList<E>为例:<>念着typeof
• ArrayList<E>中的E称为类型参数变量
• ArrayList<Integer>中的Integer称为实际类型参数
• 整个称为ArrayList<E>泛型类型
• 整个ArrayList<Integer>称为参数化的类型ParameterizedType
//使用迭代器迭代泛形集合中的元素。
@Test
public void test1(){
List<String> list = new ArrayList<String>();
list.add("aaa");list.add("bbb");list.add("ccc");
Iterator<String> it = list.iterator();
while(it.hasNext()){
String value = it.next();
System.out.println(value);
}
}
//存取HashMap中的元素
public void test2(){
Map<Integer,String> map = new HashMap<Integer,String>();
map.put(1, "aaa");
map.put(2, "bbb");
map.put(3, "ccc");
//1.用keyset
Set<Integer> set = map.keySet();
Iterator<Integer> it = set.iterator();
while(it.hasNext()){
Integer key = it.next();
String value = map.get(key);
System.out.println(key + "=" + value);
}
//2.用entryset
Set<Map.Entry<Integer, String>> set1 = map.entrySet();
Iterator<Map.Entry<Integer, String>> it1 = set1.iterator();
while(it1.hasNext()){
Map.Entry<Integer, String> me = it1.next();
int key = me.getKey();
String value = me.getValue();
System.out.println(key + "=" + value);
}
//3.用foreach
for(Map.Entry<Integer, String> me : map.entrySet()){
int key = me.getKey();
String value = me.getValue();
System.out.println(key + "=" + value);
}
}
自定义泛形——泛型方法
l Java程序中的普通方法、构造方法和静态方法中都可以使用泛型。方法使用泛形前,必须对泛形进行声明,语法:<T> ,T可以是任意字母,但通常必须要大写。<T>通常需放在方法的返回值声明之前。例如:
public static <T> void doxx(T t);
l 注意:
• 只有对象类型才能作为泛型方法的实际参数。
• 在泛型中可以同时有多个类型,例如:
public static <K,V> V getValue(K key) { return map.get(key);}
//自定义带有泛形的方法
public class Demo4 {
public <T> void run(T t){
}
public <T,E> E eat(T t){
return null;
}
}
//定义带有泛形的类
class Demo41<T> {
public void run(T t){
}
public <E> E eat(T t){
return null;
}
}
//声明泛形的注意事项:类上面声明的泛形只对类的非静态态成员有效,类上面的静态成员使用泛形,它们需单独声明
class Demo42<T> {
public void run(T t){
}
public static <T> void eat(T t){
}
}
//编写一个泛形方法,实现数组元素的交换
public <T> void swap(T arr[],int index1,int index2){
T temp = arr[index1];
arr[index1] = arr[index2];
arr[index2] = temp;
}
@Test
public void testSwap(){
Demo5 d = new Demo5();
Integer arr[] = {1,2,3};
d.swap(arr, 0, 2);
}
//编写一个泛形方法,接收一个任意数组,并颠倒数组中的所有元素
public <T> void reverse(T arr[]){
int start = 0;
int end = arr.length-1;
for(;;){
if(start>=end){
break;
}
T temp = arr[start];
arr[start] = arr[end];
arr[end] = temp;
start++;
end--;
}
}
@Test
public void testReverse(){
Demo5 d = new Demo5();
Integer arr[] = {1,2,3};
d.reverse(arr);
System.out.println(Arrays.asList(arr));
}
泛型的高级应用——有限制的通配符
l 限定通配符的上边界:
正确:Vector<? extends Number> x = new Vector<Integer>();
错误:Vector<? extends Number> x = new Vector<String>();
l 限定通配符的下边界:
正确:Vector<? super Integer> x = new Vector<Number>();
错误:Vector<? super Integer> x = new Vector<Byte>();