基礎——泛型

概述

集合可以存儲任意類型的對象,對象存入集合以後都被提升爲Object類型。當我從集合中取出對象的時候都需要進行強制轉型來後續操作對象。

 public static void main(String[] args) {
        ArrayList list = new ArrayList();
        list.add("a11");
        list.add(1);
        list.add("1");
        for (Object o : list) {
			String str = (String) o;
            System.out.println(str);
        } 
    }

這段簡單的演示代碼就會出現題java.lang.ClassCastException的運行時期異常。通常遇到這種情況可以使用instanceof來判斷是否爲某種類型或是其子類型,但這樣會增加代碼複雜度,且不能徹底解決問題,畢竟再怎麼考慮終會有所遺漏的類型不能完成正常轉換。
JDK1.5之後引入了**泛型(Generic)**語法。這樣我們在使用設計API的時候可以指定類或者方法甚至接口的泛型,設計出的API也更爲簡潔,並且也能在編譯時期查出異常。

使用泛型的好處

  1. 避免強制類型轉換問題
  2. 將運行時異常提前到編譯時期,減少了後期修改bug的工作量
  3. 一旦指定泛型,數據類型將被統一
  4. 實現代碼的模塊化,把數據類型當做參數

泛型的定義和使用

1、泛型類的定義

(1)格式

public class 類名<泛型變量>{
	...
}

泛型類中的泛型一旦被指定,整個類除了泛型方法,全部帶有泛型的類型佔位符的地方都會被替換爲指定的類型。

(2)使用範例

public class MyClass<T> {
    private T t;

    public MyClass() {
    }

    public MyClass(T t) {
        this.t = t;
    }

    public T getT() {
        return t;
    }

    public void setT(T t) {
        this.t = t;
    }
}
	public static void main(String[] args) {
        // 空參構造創造對象
        MyClass<String> mc1 = new MyClass<>();
        mc1.setT("Hello");
        String t = mc1.getT();
        System.out.println(t);
        System.out.println("-----------------------------------");
        // 滿參構造,尖括號指定了什麼類型就傳遞什麼類型數據
        MyClass<Integer> mc2 = new MyClass<>(1);
        String t = mc1.getT();
        System.out.println(t);
    }

2、泛型方法的定義

在泛型類使用中一般默認直接把泛型佔位符替換爲統一指定的類型,但有時候需要方法定義自己特定的泛型。

(1)格式

修飾符 <T> 返回值類型 方法名稱(T t,參數列表...){
	方法體...
}

注意事項:

  1. 前面<T>在定義方法的泛型
  2. 後面方法參數:T t 使用的是方法自己的的泛型

(2)使用範例

public class MyClass02<T> {
    // 不是泛型方法
    public void print(T t){
        System.out.println(t);
    }
	// 泛型方法,定義了自己的泛型,使用佔位符E表示泛型
    public <E> void show(E e){
        System.out.println(e);
    }
}
public static void main(String[] args) {
        MyClass02<String> mc = new MyClass02<>();
        mc.print("Hello");
        // print 使用的是類上的泛型,已經確定爲String類型了
        //錯誤:mc.print(10000);
        mc.show(18);
    }

3、泛型接口的定義

(1)格式

public interface 接口名稱<T> {
	...
}

(2)接口上定義的泛型,什麼時間確定具體類型

  1. 定義實現類時,直接確定接口上泛型的具體類型
public interface MyInter<T> {
    public abstract void show(T t);
}
/*
    定義實現類時,直接確定接口上泛型的具體類型
 */
public class MyInterImpl implements MyInter<String> {
    @Override
    public void show(String s) {
        System.out.println(s);
    }
}

  1. 定義實現類時不確定接口上泛型的具體類型,那麼該實現類必須定義爲泛型類,而且實現類上的泛型變量要和接口上的泛型變量名稱一致——創建實現類對象時,<>中要寫是什麼類型,泛型就是什麼類型。這也叫做泛型的傳遞
public interface MyInter<T> {
    public abstract void show(T t);
}
/*
定義實現類時不確定接口的類型
*/
public class MyInterImplB<T> implements MyInter<T> {
    @Override
    public void show(T t) {
        System.out.println(t);
    }
}

泛型的通配符

代表任意的一種引用類型,只能用來匹配泛型,不能用來定義泛型。

(1)注意事項

  1. 泛型是不存在多態的,創建集合對象時,左右兩邊的<>內容要保持一致
  2. ArrayList< ? > list 可以接受什麼?
    可以接受ArrayList的任意類型對象(只要在創建ArrayList集合對象中<>寫上一種引用類型,都是可以的)

(2)示例

	public static void main(String[] args) {
        ArrayList<String> l1 = new ArrayList<>();
        Collections.addAll(l1,"123-123qwe-qwe-qwe-fhj-ghj-yjw-wlt-gfd-iop".split("-"));
        ArrayList<Integer> l2 = new ArrayList<>();
        Collections.addAll(l2,4,5,6,13,123,123,123,45,6,7,12,890,7,2,3,30);
        print(l1);
        print(l2);
    }

    public static void print(ArrayList<?> arrayList){
        System.out.println(arrayList);
    }

泛型的上下限

使用泛型的通配符,我們可以接受任何類型的參數,但有時候我們需要更加嚴格的限制傳遞的類型,比如,我們需要傳遞的參數是某個類型或者它的子類,又或是某個類型或者它的父類。此時,我們就需要使用泛型的上下限來做約束。

1、泛型的上限:

格式-類型名稱<? extends E> :表示E類型或者E類型任意子類

2、泛型的下限:

格式-類型名稱<? super E>:表示E類型或者E類型的任意父類

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