泛型概念:
泛型(Generic)一、泛型的作用JDK5中的泛形允许程序员在编写集合代码时,就限制集合的处理类型,从而把原来程序运行时可能发生问题,转变为编译时的问题,以此提高程序的可读性和稳定性(尤其在大型程序中更为突出)。注意:泛型是提供给javac编译器使用的,它用于限定集合的输入类型,让编译器在源代码级别上,即挡住向集合中插入非法数据。但编译器编译完带有泛形的 java程序后,生成的class文件
泛型的作用
JDK5中的泛形允许程序员在编写集合代码时,就限制集合的处理类型,从而把原来程序运行时可能发生问题,转变为编译时的问题,以此提高程序的可读性和稳定性(尤其在大型程序中更为突出)。
注意:泛型是提供给javac编译器使用的,它用于限定集合的输入类型,让编译器在源代码级别上,即挡住向集合中插入非法数据。但编译器编译完带有泛形的 java程序后,生成的class文件中将不再带有泛形信息,以此使程序运行效率不受到影响,这个过程称之为“擦除”。
泛形的基本术语,以ArrayList<E>为例:
<>念着typeof
ArrayList<E>中的E称为类型参数变量
ArrayList<Integer>中的Integer称为实际类型参数
整个称为ArrayList<E>泛型类型
整个ArrayList<Integer>称为参数化的类型ParameterizedType
使用泛形时,泛形类型须为引用类型,不能是基本数据类型
自定义泛型:泛型方法
Java程序中的普通方法、构造方法和静态方法中都可以使用泛型。方法使用泛形前,必须对泛形进行声明,语法:<T> ,T可以是任意字母,但通常必须要大写。<T>通常需放在方法的返回值声明之前。例如:public static <T> void doxx(T t);
1.编写一个泛形方法,实现数组元素的交换
public static <T> void test1(T arr[],int i,int j){
T temp=arr[i];
arr[i]=arr[j];
arr[j]=temp;
}
public static void main(String[] args) {
Integer arr[]={1,2,3,4};
test1(arr,0,2);
for(int ar:arr){
System.out.println(ar);
}
}
2.编写一个泛形方法,接收一个任意数组,并颠倒数组中的所有元素
public static<T> void test2(T arr[]){
int startindex=0;
int endindex=arr.length-1;
for(;;){
if(startindex>=endindex){
break;
}
Ttemp=arr[startindex];
arr[startindex]=arr[endindex];
arr[endindex]=temp;
startindex++;
endindex--;
}
}
public static void main(String[] args) {
Integer arr[]={1,2,3,4};
test2(arr);
for(int ar:arr){
System.out.println(ar);
}
}
注意
只有对象类型才能作为泛型方法的实际参数。
在泛型中可以同时有多个类型,例如:
public static<K,V> V getValue(K key) {
return map.get(key);
}
如果一个类多处都要用到同一个泛型,这时可以把泛形定义在类上(即类级别的泛型),语法格式如下:
public class GenericDao<T> {
private T field1;
public void save(T obj){
}
public T getId(int id){
}
}
静态方法不能使用类定义的泛形,而应单独定义泛形
泛型应用举例:
package com.generictest.learning;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
/**
* 泛型的使用举例
* @author Administrator
*
*/
public class GenericTest {
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
Map<String, Integer> man=new HashMap<String, Integer>();
man.put("A", 1);
man.put("B", 2);
man.put("C", 3);
/**
* 通过反射向定义为泛型的Map集合添加元素可以绕过泛型的限制
* 因为泛型是在编译时起作用的,在运行时所有定义的泛型都被JVM去掉
*/
try {
man.getClass().getMethod("put", new Class[]{Object.class,Object.class}).invoke(man, new Object[]{"D","maolei"});
}catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
/**
* 把Map转换为Set集合
* 此处可以证明泛型是可以嵌套的
*/
Set<Entry<String, Integer>> mn = man.entrySet();
for(Entry<String, Integer> en:mn){
System.out.println(en.getKey()+":"+en.getValue());
}
/**
* 泛型不是协变的
* 关于泛型的混淆,一个常见的来源就是假设它们像数组一样是协变的。
* 其实它们不是协变的。List<Object>不是List<String>的父类型。
*/
List<Integer> in=new ArrayList<Integer>();
//List<Number> nu=in; 编译不通过
Integer[] intArray = new Integer[10];
Number[] numberArray = intArray;
List<String> st=new ArrayList<String>();
st.add(new String("maolei"));
st.add(new String("maoqingshan"));
//可以将带泛型的对象传递给一个不带泛型的形式变量
printList(st);
List l=st;//是合法的因为:List<String>是一个List
printList2(l);//是合法的因为:List是一个List<Object>
//printList2(st);//编译不通过因为:List<String>不是一个List<Object>
/**
* printList3采用通配符的泛型
* 可以接受任何泛型类型的集合
*/
printList3(st); //合法的
List<Integer> itg=new ArrayList<Integer>();
itg.add(new Integer(1));
itg.add(new Integer(2));
printList3(itg);//合法的
/**
* 下面的代码则工作得很好
* 对于lu,编译器一点都不知道List的类型参数的值。但是编译器比较聪明,它可以做一些类型推理,
* 它推断未知的类型参数 必须扩展Object。这个特定的推理没有太大的跳跃,但是编译器可以作出一些非常令人佩服的类型推理。
* 所以它让您调用List.get()并推断返回类型为Object。
*/
List<Integer> li = new ArrayList<Integer>();
li.add(new Integer(42));
List<?> lu = li;
System.out.println(lu.get(0));
/**
* 对于lm,编译器不能对List的类型参数作出足够严密的推理,以确定将Integer传递给List.add()是类型安全的。
* 所以编译器将不允许您这么做。
* 对于lm不能操作与泛型有关的方法,add(E e)是与泛型有关的,clear()是与泛型无关的
*/
List<Integer> ll = new ArrayList<Integer>();
ll.add(new Integer(42));
List<?> lm = ll;
//lm.add(new Integer(43)); // error
lm.clear();//可以正常编译
/**
* 泛型方法的使用
*/
Object rtnValue1 = ifThenElse(true,new String("maolei"),new Integer(12));
Object rtnValue2 = ifThenElse(false,new Date(),new HashMap<String, Integer>());
Object rtnValue3 = ifThenElse(true, "pi", new Float(3.14));
String rtnValue4=ifThenElse(false, new String("a"), new String("b"));
Integer rtnValue5=ifThenElse(true, new Integer(12), new Integer(21));
//String rtnValue6=ifThenElse(false, new String("a"), new Integer(2));//编译不通过
System.out.println(rtnValue1);
System.out.println(rtnValue2);
System.out.println(rtnValue3);
System.out.println(rtnValue4);
System.out.println(rtnValue5);
}
/**
* 不采用泛型打印一个集合
* @param l
*/
private static void printList(List l){
Iterator it = l.iterator();
while(it.hasNext()){
System.out.println(it.next());
}
}
/**
* 采用泛型打印一个集合
* @param l
*/
private static void printList2(List<Object> l){
Iterator it = l.iterator();
while(it.hasNext()){
System.out.println(it.next());
}
}
/**
* 采用通配符的泛型打印一个集合
* List<?>是任何泛型List的父类型,所以您完全可以将 List<Object>、List<Integer>或 List<List<List<Flutzpah>>>传递给printList()
* @param l
*/
private static void printList3(List<?> l){
Iterator it = l.iterator();
while(it.hasNext()){
System.out.println(it.next());
}
}
/**
* 泛型方法
* 根据它的第一个参数的布尔值,它将返回第二个或第三个参数
* 为什么要用泛型方法:
* 1:当泛型方法是静态的时,这种情况下不能使用类类型参数。
* 2:当T上的类型约束对于方法真正是局部的时,
* 这意味着没有在相同类的另一个方法签名中使用相同类型 T 的约束。
* 通过使得泛型方法的类型参数对于方法是局部的,可以简化封闭类型的签名。
* @param b
* @param first
* @param second
* @return
*/
private static <T> T ifThenElse(boolean b, T first, T second) {
return b ? first : second;
}
}