Java 泛型


1、泛型的由來

  我們先看下面這段代碼:

                List list = new ArrayList();
		list.add(24);  //向集合中添加一個 Integer 類型的數據
		list.add("Tom");	//向集合中添加一個 String 類型的數據
		
		for(int i = 0 ; i < list.size() ; i++){
			Object obj = list.get(i);  //注意這里每個類型都是 Object
			System.out.println(obj);
		}
		
		//如果我們遍歷的時候就想得到自己想要的數據類型
		for(int i = 0 ; i < list.size() ; i++){
			String obj = (String) list.get(i);  //在取 Integer 的時候會報類型轉換錯誤
			System.out.println(obj);
		}

  報錯信息如下:

  也就是 集合中第二個數據是 Integer,但是我們取出來的時候將其轉換為 String 了,所以報錯。

  那么這個如何解決呢?

  ①、我們在遍歷的時候,根據每個數據的類型判斷,然后進行強轉。

那么我們說這個集合只有兩條數據,我們可以進行判斷強轉,如果數據有成千上萬條呢,我們都通過這樣判斷強轉肯定不可取

  ②、在往集合中加入數據的時候,我們就做好限制,比如這個集合只能添加 String 類型的;下一個集合只能添加 Integer 類型的,那么我們在取數據的時候,由於前面已經限制了該集合的數據類型,那么就很好強轉了。

  這第二種解決辦法,也就是我們這篇文章講的 泛型

 

2、什么是泛型?

  泛型是Java SE 1.5的新特性,泛型的本質是參數化類型,也就是說所操作的數據類型被指定為一個參數。這種參數類型可以用在類、接口和方法的創建中,分別稱為泛型類、泛型接口、泛型方法。

  在Java SE 1.5之前,沒有泛型的情況的下,通過對類型Object的引用來實現參數的“任意化”,“任意化”帶來的缺點是要做顯式的強制類型轉換,而這種轉換是要求開發者對實際參數類型可以預知的情況下進行的。對於強制類型轉換錯誤的情況,編譯器可能不提示錯誤,在運行的時候才出現異常,這是一個安全隱患。

 

3、泛型的基本用法

  3.1 對於上面的問題我們只需要將上述代碼的 List list = new ArrayList()  改為   List<String> list = new ArrayList<String>();

                List<String> list = new ArrayList<String>();
		//list.add(22);  //向集合中添加一個 Integer 類型的數據時,編譯器會報錯
		list.add("Bob");  //向集合中添加一個 String 類型的數據
		list.add("Tom");	//向集合中添加一個 String 類型的數據
		
		//如果我們遍歷的時候就想得到自己想要的數據類型
		for(int i = 0 ; i < list.size() ; i++){
			String obj = list.get(i);  //這里就不需要強轉了,前面添加的是什么類型,這里獲取的就是什么類型
			System.out.println(obj);
		}            

  

  

  3.2 泛型是在編譯階段有效

 

        List<String> list1 = new ArrayList<String>();
	List list2 = new ArrayList();
	Class c1 = list1.getClass();
	Class c2 = list2.getClass();
	System.out.println(c1==c2); //true    

 

  上述代碼,由於我們知道反射是在運行時階段,c1==c2為 true,說明了編譯之后的 class 文件中是不包含任意的泛型信息的。如果不信,我們可以看 class 文件的反編譯信息

        java.util.List list1 = new ArrayList();
        java.util.List list2 = new ArrayList();
        Class c1 = list1.getClass();
        Class c2 = list2.getClass();
        System.out.println(c1.equals(c2));

  我們可以看到 反編譯之后的 list1和 list2完全一樣。

  結論:Java 泛型只在編譯階段有效,即在編譯過程中,程序會正確的檢驗泛型結果。而編譯成功后,class 文件是不包含任何泛型信息的

 

 

  3.3 泛型類和泛型方法

 

public class Box<T> {
	private T box;
	public T getBox(T t){
		this.box = t;
		return t;
	}
	public void getType(){
		System.out.println("T的實際類型為:"+box.getClass().getName());
	}
	
	public static void main(String[] args) {
		Box box = new Box();
		System.out.println(box.getBox(1));
		box.getType();
		
		System.out.println(box.getBox("Tom"));
		box.getType();
	}

}

 

  輸出結果為:

1
T的實際類型為:java.lang.Integer
Tom T的實際類型為:java.lang.String

  

  3.4 泛型通配符

  在泛型中,我們可以用 ? 來代替任意類型

 

     public List wildCard(List<?> list){
		
		return list;
	}

	public static void main(String[] args) {
		GenericTest gt = new GenericTest();
		//構造一個 Interger 類型的集合
		List<Integer> integer = new ArrayList<Integer>();
		integer.add(1);
		System.out.println(gt.wildCard(integer));
		//構造一個 String 類型的集合
		List<String> str = new ArrayList<String>();
		gt.wildCard(str);
		//構造一個 Object 類型的集合
		List<Object> obj = new ArrayList<Object>();
		obj.add(1);
		obj.add("a");
		System.out.println(gt.wildCard(obj));
		//構造一個 任意類型的 集合,這和 List<Object> 存放數據沒啥區別
		List list = new ArrayList();
		gt.wildCard(list);
		
	}

 

  

 

  3.5 泛型的上限和下限

  ①、上限: 語法(? extends className),即只能為 className 或 className 的子類

//通配符的下限,只能是 Number 或 Number的子類
	public List wildCard(List<? extends Number> list){
		
		return list;
	}

	public static void main(String[] args) {
		GenericTest gt = new GenericTest();
		//構造一個 Interger 類型的集合
		List<Integer> integer = new ArrayList<Integer>();
		integer.add(1);
		System.out.println(gt.wildCard(integer));
		//構造一個 String 類型的集合
		List<String> str = new ArrayList<String>();
		//gt.wildCard(str);   //編譯報錯
		//構造一個 Object 類型的集合
		List<Object> obj = new ArrayList<Object>();
		obj.add(1);
		obj.add("a");
		//System.out.println(gt.wildCard(obj)); //編譯報錯
		
	}

  ①、下限: 語法(? super className),即只能為 className 或 className 的父類

//通配符的上限,只能是 Number 或 Number的父類
	public List wildCard(List<? super Number> list){
		
		return list;
	}

	public static void main(String[] args) {
		GenericTest gt = new GenericTest();
		//構造一個 Interger 類型的集合
		List<Integer> integer = new ArrayList<Integer>();
		integer.add(1);
		//System.out.println(gt.wildCard(integer));  //編譯報錯
		//構造一個 String 類型的集合
		List<String> str = new ArrayList<String>();
		//gt.wildCard(str);   //編譯報錯
		//構造一個 Object 類型的集合
		List<Object> obj = new ArrayList<Object>();
		obj.add(1);
		obj.add("a");
		System.out.println(gt.wildCard(obj)); 
	}

  

4、泛型的注意事項

  4.1、不能用基本類型來定義泛型,如 int、float

List<int> list = new ArrayList<int>(); //不能用 int 這樣的基本類型定義泛型

  關於這一點很好想明白,因為 集合中只能存放引用類型的數據,即使你存入基本類型的,Java還是會通過自動拆箱和自動裝箱機制將其轉換為引用類型

  

  4.2、如果使用 ? 接收泛型對象時,則不能設置被泛型指定的內容

List<?> list = new ArrayList<>();
		list.add("aa");  //錯誤,無法設置

  

  4.3、泛型方法的定義與其所在的類是否是泛型類是沒有任何關系的,所在的類可以是泛型類,也可以不是泛型類

 

  4.4、泛型類沒有繼承關系,即String 為 Object 類型的子類,則 List<String> 是 List<Object> 的子類這句話是錯誤的

  原因:假設上面那句話是正確的,那么由於泛型的產生機制就是放什么類型的數據進去,取出來的就是什么類型,而不用進行類型轉換,這里把 String 類型的數據放入Object 類型的泛型集合中,那么取出來的應該就是 String      類型的數據,而實際上取出來的是 Object 類型的數據,這與泛型的產生機制相違背,故不成立!

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM