泛型(Generic) —泛形的作用
- JDK5以前,对象保存到集合中就会失去其特性,取出时通常要程序员手工进行类型的强制转换,这样不可避免就会引发程序的一些安全性问题。
- JDK5中的泛形允许程序员在编写集合代码时,就限制集合的处理类型,从而把原来程序运行时可能发生问题,转变为编译时的问题,以此提高程序的可读性和稳定性(尤其在大型程序中更为突出)。
- 注意:泛型是提供给javac编译器使用的,它用于限定集合的输入类型,让编译器在源代码级别上,即挡住向集合中插入非法数据。但编译器编译完带有泛形的java程序后,生成的class文件中将不再带有泛形信息,以此使程序运行效率不受到影响,这个过程称之为“擦除”。
- 泛形的基本术语,以ArrayList<E>为例:<>念着typeof
- ArrayList<E>中的E称为类型参数变量
- ArrayList<Integer>中的Integer称为实际类型参数
- 整个称为ArrayList<E>泛型类型
- 整个ArrayList<Integer>称为参数化的类型ParameterizedType
泛型典型应用
- 使用迭代器迭代泛形集合中的元素。
- 使用增强for循环迭代泛形集合中的元素。
- 存取HashMap中的元素。
- 使用泛形时的几个常见问题:
- 使用泛形时,泛形类型须为引用类型,不能是基本数据类型
- ArrayList<String>list = new ArrayList<Object>(); //这个代码是错误的,在存的时候凡是Object的子类对象都能存,在取的时候会把你存入的对象都当做Integer来处理,会出错。
- ArrayList<Object>list = new ArrayList<String>(); //编译时报错
- ArrayList<String>list = new ArrayList (); //可以通过
- ArrayList list= new ArrayList<String>(); //可以通过
- 总结:一旦用上了泛型,两边的类型就应该保持一致,要么两边只用一边。
package cn.mengmei.generic;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.junit.Test;
public class Demo1 {
@Test
public void test1(){
List<Integer> list = new ArrayList<Integer>();
list.add(1);
int num = list.get(0);
}
@Test
public void test2(){
Map<String,Integer> map = new HashMap<String,Integer>();
map.put("a", 30);
map.put("b", 50);
Set<Map.Entry<String, Integer>> set = map.entrySet();
for(Map.Entry<String, Integer> me : set){
String key = me.getKey();
Integer value = me.getValue();
System.out.println(key+"="+value);
}
}
//一旦用上了泛型,两边的类型就应该保持一致,要么两边只用一边。
@Test
public void test3(){
/*
ArrayList<Integer> a = new ArrayList<Object>(); //编译时报错。
ArrayList<Object> b = new ArrayList<Integer>(); //编译时报错。
*/
//为了向以前版本的jdk兼容,为了新程序员可以调用老程序员写的方法,所以必须支持这种写法。
ArrayList<String> c = new ArrayList();
//例:
List<Integer> l1 = t1();
//为了初级程序员可以调用高级程序员写的方法,所以必须支持这种写法。
ArrayList d = new ArrayList<String>();
//例:
List l2 = t2();
}
public List t1(){
return new ArrayList();
}
public List<Integer> t2(){
return new ArrayList<Integer>();
}
}
自定义泛形——泛型方法
- Java程序中的普通方法、构造方法和静态方法中都可以使用泛型。方法使用泛形前,必须对泛形进行声明,语法:<T> ,T可以是任意字母,但通常必须要大写。<T>通常需放在方法的返回值声明之前。例如:
- public static <T> void doxx(T t);
- 练习:
- 编写一个泛形方法,实现数组元素的交换。
- 编写一个泛形方法,接收一个任意数组,并颠倒数组中的所有元素。
- 注意:
- 只有对象类型才能作为泛型方法的实际参数。
- 在泛型中可以同时有多个类型,例如:
- public static <K,V> V getValue(K key) { return map.get(key);}
package cn.mengmei.generic;
import java.util.Arrays;
public class Demo2 {
public static void main(String[] args) {
//原数组
Integer[] arr = new Integer[]{1,2,3,4,5};
System.out.println(Arrays.asList(arr));
//交换位置1和位置2的两个元素
swap(arr,1,2);
System.out.println(Arrays.asList(arr));
//从小到大排序
sort(arr);
System.out.println(Arrays.asList(arr));
//原数组
String[] arr1 = new String[]{"a","b","c","d","e"};
System.out.println(Arrays.asList(arr1));
//反转数组
reverse(arr1);
System.out.println(Arrays.asList(arr1));
//从小到大排序
sort(arr1);
System.out.println(Arrays.asList(arr1));
}
//交换数组中的任意两个位置的元素
public static <T> void swap(T[] arr, int pos1, int pos2){
T temp = arr[pos1];
arr[pos1] = arr[pos2];
arr[pos2] = temp;
}
//颠倒数组中的所有元素
public static <T> void reverse(T[] arr){
for(int x=0,y=arr.length-1; x<y; x++,y--){
swap(arr,x,y);
}
}
//给数组元素由小到大排序
public static <T extends Comparable<T>> void sort(T[] arr){
for(int x=0; x<arr.length-1; x++){
for(int y=x+1; y<arr.length; y++){
if( ( arr[x].compareTo(arr[y]) ) > 0){
swap(arr,x,y);
}
}
}
}
}
运行结果:
[1, 2, 3, 4, 5]
[1, 3, 2, 4, 5]
[1, 2, 3, 4, 5]
[a, b, c, d, e]
[e, d, c, b, a]
[a, b, c, d, e]
package cn.mengmei.generic;
//在类上定义的泛型,只对类的非静态成员有效。
public class Demo3<T> {
public void run(T t){
}
public void eat(T t){
}
//为什么在类上定义的泛型,只对类的非静态成员有效?
//因为调用静态成员不需要new对象,没有new对象在类上定义的泛型T就没有指定。在调用这个方法的时候,这个T是啥别人就不知道。
//为了编译通过,在方法上对T进行定义。
public static <T> void test(T t){
}
}
基础加强还没讲完,要在二十几天的时候还要讲一天。