Java 泛型

一、泛型的由來java

  咱們先看下面這段代碼:安全

                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);
		}

  報錯信息以下:this

  也就是 集合中第二個數據是 Integer,可是咱們取出來的時候將其轉換爲 String 了,因此報錯。spa

  那麼這個如何解決呢?對象

  ①、咱們在遍歷的時候,根據每一個數據的類型判斷,而後進行強轉。blog

那麼咱們說這個集合只有兩條數據,咱們能夠進行判斷強轉,若是數據有成千上萬條呢,咱們都經過這樣判斷強轉確定不可取繼承

  ②、在往集合中加入數據的時候,咱們就作好限制,好比這個集合只能添加 String 類型的;下一個集合只能添加 Integer 類型的,那麼咱們在取數據的時候,因爲前面已經限制了該集合的數據類型,那麼就很好強轉了。接口

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

 

二、什麼是泛型?get

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

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

 

三、泛型的基本用法

  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.一、不能用基本類型來定義泛型,如 int、float

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

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

  

  4.二、若是使用 ? 接收泛型對象時,則不能設置被泛型指定的內容

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

  

  4.三、泛型方法的定義與其所在的類是不是泛型類是沒有任何關係的,所在的類能夠是泛型類,也能夠不是泛型類

 

  4.四、泛型類沒有繼承關係,即String 爲 Object 類型的子類,則 List<String> 是 List<Object> 的子類這句話是錯誤的

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

相關文章
相關標籤/搜索