JavaSE-泛型機制

泛型是java編程中經常使用,且很有用的一個特性,經常在集合與DAO(數據庫訪問對象)編程中使用。

1、爲什麼要使用泛型:

在沒有使用泛型時,集合存儲和提取數據經常出現兩個不足:

1.存儲數據時,不能保證數據的類型安全,也就是說,同一集合中可以存儲多種類型的數據

2.在集合中獲取數據時,必須經過強制轉換,才能轉換爲自己需要類型的數據,而且由於第一條的不足,導致會出現類型轉換異常

這就相當於繞了一個彎,把原本的數據類型--->轉換爲Object類型存儲--->轉換爲原本的數據類型以便使用。

代碼:
	//在沒有使用泛型時:
	//1、不能保證存儲數據類型的安全
	//2、數據需要強轉,可能出現ClassCastExcepetion異常
	@Test
	public void testNoGeneric(){
		List list = new ArrayList();
		list.add(88);
		list.add(90);
		list.add(99);
		
		list.add("AA");//不能保證存儲數據類型安全
		
		for(int i=0;i<list.size();i++){
			int score = (int) list.get(i);//拋出ClassCastException異常
			System.out.println(score);
		}
	}

java 的泛型機制就是解決這麼樣的問題,通過使用泛型:

1.保證數據存儲類型的安全,就是通過指定泛型的類型,使得在存儲數據時,只能存儲先前指定類型

2.避免了數據類型的轉換,也就避免了異常

代碼:
	//使用泛型,保證數據存儲的類型安全
	//同時,避免了強制類型轉換
	@Test
	public void testGeneric1(){
		//在指定泛型之後,該泛型類的所有有關泛型的方法或返回類型,都變成了指定的泛型類型
		List<Integer> list = 
				new ArrayList<Integer>();//實現類的泛型可以不再指定,只需一個<>即可
		list.add(88);
		list.add(90);
		list.add(99);
		//list.add("AA");//編譯時就會報錯,保證數據存儲類型的安全
		
		//List容器的遍歷方法
		Iterator<Integer> it = list.iterator();
		while(it.hasNext()){
			Integer score = it.next();
			System.out.println(score);
		}
		//或者
//		for(Integer i:list){
//			System.out.println(i);
//		}
	}

2、在集合中使用泛型:

其實通過指定泛型,泛型類的所有與泛型相關的方法都自動變成了指定的類型

集合List:見“爲什麼使用泛型”一節

集合Map:
	@Test
	public void test3(){
		Map<String, Integer> map = 
				new HashMap<String,Integer>();//OR new HashMap<>()即可
		map.put("AA", 78);
		map.put("BB", 88);
		map.put("CC", 99);
		
		Set<Entry<String, Integer>> set = map.entrySet();
		for(Entry<String, Integer> o:set){
			System.out.println(o);
		}
	}

3、自定義泛型類、泛型接口和泛型方法

自定義泛型類

通過添加<T>在類型名後邊:class ClassName<T>

自定義泛型類Order,

代碼:

public class Order<T> {


	private String orderName;
	private int orderId;
	private T t;
	public List<T> list = new ArrayList<>();
	
	public void add(){
		list.add(t);
	}


 	public String getOrderName() {
		return orderName;
	}
	public void setOrderName(String orderName) {
		this.orderName = orderName;
	}
	public int getOrderId() {
		return orderId;
	}
	public void setOrderId(int orderId) {
		this.orderId = orderId;
	}
	public T getT() {
		return t;
	}
	public void setT(T t) {
		this.t = t;
	}
	@Override
	public String toString() {
		return "Order [orderName=" + orderName + ", orderId=" + orderId + ", t=" + t + "]";
	}
	
	//泛型方法,泛型方法的類型與泛型類無關
	public <E> E getE(E e){
		return e;
	}
	//泛型方法,實現數組到集合的複製
	public <E> List<E> fromArrayToList(E[] arr, List<E> list){
		for(E e:arr){//數組也可以這樣遍歷
			list.add(e);
		}
		return list;
	}
}

Order的使用,

代碼:

	//自定義泛型類的使用
	@Test
	public void test4(){
		Order<Boolean> order = new Order<Boolean>();
		order.setT(true);
		Boolean b = order.getT();
		System.out.println(b);
		
		order.add();
		List<Boolean> list = order.list;
		System.out.println(list);
		
	}

1.在實例化泛型類對象時,指定泛型的類型,對應的類中所有使用泛型的位置,都變爲實例化中指定的泛型的類型

2.如果我們定義了泛型類,但在實例化中未指定泛型類型,那麼默認是Object類型


子類繼承泛型類,可以指定泛型類型,也可以保持子類繼續是泛型類:

代碼:

//定義泛型類的子類時,可以指明泛型類型,通過在父類後指定類型
class subOrder extends Order<Boolean>{
}
//也可以不指定,子類也還是泛型,子類父類都需指定泛型<T>
class subOrder2<T> extends Order<T>{
	
}


泛型接口同泛型類;


泛型方法

泛型方法其實與泛型類無關,泛型方法可以在泛型類中,也可以在非泛型類中。

泛型方法通過權限修飾符後添加泛型<E>,使函數方法成爲泛型方法,參看Order類中兩個泛型方法getE()和fromArrayToList()

	//泛型方法,泛型方法的類型與泛型類無關
	public <E> E getE(E e){
		return e;
	}
	//泛型方法,實現數組到集合的複製
	public <E> List<E> fromArrayToList(E[] arr, List<E> list){
		for(E e:arr){//數組也可以這樣遍歷
			list.add(e);
		}
		return list;
	}

泛型方法的使用:(其實通過參數,便指定類泛型方法的泛型類型)
代碼:
	//泛型方法的使用
	@Test
	public void test5(){
		Order<Boolean> order = new Order<Boolean>();
		//調用泛型方法
		Integer i = order.getE(50);//指定參數,返回類型確定爲Integer
		Double d = order.getE(4.3);//返回類型爲Double
		
		Integer[] ints = new Integer[]{1,2,3};
		List<Integer> list = new ArrayList<>();
		List<Integer> list3 = order.fromArrayToList(ints, list);
		System.out.println(list3);
	}

4、泛型與繼承的關係

類之間存在繼承關係,String類是Object類的子類,那麼泛型中List<String>與List<Object>存在怎麼樣的關係呢?

List<String>與List<Object>是並列關係,不存在繼承關係:

	/*
	 * List<Object>與List<String>不存在子父類關係,屬於並列關係
	 * 他們有共同的父類:List<?>
	 */
	@Test
	public void test6(){
		Object obj = null;
		String str = "AA";
		obj = str;//沒問題,子類賦給父類
		
		Object[] objs = null;
		String[] strs = new String[]{"AA","BB","CC"};
		objs = strs;//沒問題,子類數組賦給父類數組
		
		List<Object> listObject=null;
		List<String> listString = new ArrayList<String>();
		listObject = listString;//編譯不通過,說明List<Object>與List<String>不存在子父類關係

	}
其實,List<String>與List<Object> 有共同的父類:List<?> ,這就是通配符?的作用,看下節。

5、通配符

通配符,就是應對泛型類之間的繼承關係的,分三種:

List<?> 是所有List<ClassName>的父類

代碼:

	/**
	 * 通配符?
	 * List<?> 是所有List<Object>、List<String> ... 的父類
	 */
	public void test7(){
		
		List<?> list=null;//共同的父類,通配符?
		List<Object> listObject = new ArrayList<Object>();
		List<String> listString = new ArrayList<String>();
		list = listObject;
		list = listString;
		
		show(listObject);
		show(listString);
	}

List<? extends A>是List<A類及其子類> 的父類

List<? super B> 是List<B類及其父類> 的父類

	/*
	 * List<? extends A>是所有A類及其子類的父類,即:
	 * 	     可以存放A的所有子類及其本身ClassName類;
	 * List<? super B> 是所有B類及其父類的父類,即:
	 *    可以存放所有B的父類及B類;
	 */
	public void test8(){
		List<? extends Number> list1 = null;
		List<Integer> list2= null;
		list1 = list2;//可以,因爲Integer是Number的子類
		
		List<Object> list3 = null;
//		list1 = list3;//編譯不通過,因爲Object 是Number的父類
		
		List<? super Number> list4 = null;
		list4 = list3;//可以
	}

對於通配符的泛型集合List<?> ,是可以進行遍歷查看操作的,但是不允許進行寫入,只可以寫入null

	@Test
	public void test9(){
		List<String> list = new ArrayList<>();
		list.add("AA");
		list.add("BB");
		list.add("CC");
		
		
		List<?> list1= list;
		//可以查看遍歷聲明通配符List<?>中的元素
		Iterator<?> it = list1.iterator();
		while(it.hasNext()){
			System.out.println(it.next());
		}
		
		//不可以向聲明爲通配符的泛型List<?>中添加元素,唯一例外的是null
//		list1.add("AA");//編譯不通過
		list1.add(null);
	}

6、其他注意點

1.靜態方法中不能使用類的泛型

在泛型類中,靜態方法中不能使用泛型,因爲靜態方法在類加載時就加載了,此時還未指定泛型類的類型,不能使用。

但是在靜態方法中可以使用泛型方法,即結合靜態方法與泛型方法:

	public static <E> E test(E e){
		return e;
	}

2.如果泛型類是一個接口或者抽象類,則不可創建泛型類的對象

接口和抽象類本身就不可以創建類對象,跟泛型無多大關係

3.不能在catch語句中使用泛型

在泛型類中,可能會寫到try-catch語句,進行異常的處理,但是泛型T是不可以在catch語句中使用的。
不能在catch語句中使用,是指catch(T t)是編譯不通過的:
	public void test(){
		try{
			
		}catch(T e){//編譯錯誤
			
		}
	}
但是這樣使用是可以的:
	public void test(){
		try{
			T t;
		}catch(Exception e){
			T t;
		}
	}







發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章