Java(7)泛型

1、泛型概述

一、什麼是泛型

  • 泛型就是標籤,加了泛型,就至關於加了標籤,這個容器就只能放這一類物品。java

  • 把元素的類型設參數,這個類型參數叫作泛型。Collection<E>,List<E>,ArrayList<E> 這個<E>就是類型參數,即泛型。數據庫

  • 所謂泛型,就是容許在定義類、接口時經過一個標識表示類中某個屬性的類型或者是某個方法的返回值及參數類型。這個類型參數將在使用時(例如, 繼承或實現這個接口,用這個類型聲明變量 、建立對象時)肯定(即傳入實 際的類型參數,也稱爲類型實參)。數組

  • JDK1.5時引入。安全

二、爲何用泛型

  • 存儲時:編譯時進行類型檢查來保證,存放的都是類型同樣的數據,更安全。
  • 獲取時:獲取的都是類型同樣的數據,無序強轉

三、在集合中使用泛型

  • ① 集合接口或集合類在jdk5.0時都修改成帶泛型的結構。
  • ② 在實例化集合類時,能夠指明具體的泛型類型。ide

  • ③ 指明完之後,在集合類或接口中凡是定義類或接口時,內部結構(好比:方法、構造器、屬性等)使用到類的泛型的位置,都指定爲實例化的泛型類型。this

    好比:add(E e) --->實例化之後:add(Integer e)。code

  • ④ 注意點:泛型的類型必須是類,不能是基本數據類型。須要用到基本數據類型的位置,拿包裝類替換。對象

  • ⑤ 若是實例化時,沒有指明泛型的類型。默認類型爲java.lang.Object類型。繼承

  • ⑥在JDK7中新特性:類型推斷接口

    • List<String> list = new List<>();後面的泛型能夠省略

2、自定義泛型結構

一、泛型類、接口

  • 自定義泛型類代碼實現:核心思想就是把T當作一個某某類型的參數,只不過須要在尖括號標明
/**
 * 自定義泛型類
 */
public class MyGeneric<T> {
    String name;
    int age;
    T decs;//能夠把他當作一個類來看,可是並非類,而是參數

    public MyGeneric() {
    }

    //帶參構造器
    public MyGeneric(String name, int age, T decs) {
        this.name = name;
        this.age = age;
        this.decs = decs;
    }

    //get()方法
    public T getDecs() {
        return decs;
    }

    //set()方法
    public void setDecs(T decs) {
        this.decs = decs;
    }

    //toString()方法
    @Override
    public String toString() {
        return "MyGeneric{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", decs=" + decs +
                '}';
    }
}
  • 子類繼承父類時,泛型的狀況
    • 說明:子類是「富二代」,除了指定或保留父類的泛型,還能夠增長本身的泛型。
    • 狀況一:子類不保留父類的泛型
      • 父類沒有類型,擦除,類型按照Object處理
      • 父類是具體類型
    • 狀況二:子類保留父類的類型
      • 所有保留
      • 部分保留
    • 代碼示例
class Father<T1, T2>{
}

//子類不保留父類的泛型
//1)沒有類型,擦除--->這時,子類不是泛型
class Son1 extends Father{//等價於class Son extends Father<Object,Object>{
}

//2)具體類型--->這時,子類不是泛型
class son2 extends Father<Integer, String>{
}

//子類保留父類的泛型
//1)所有保留
class Son3<T1, T2> extends Father<T1, T2>{
}
//2)部分保留
class Son4<T2> extends Father<Integer, T2>{
}

//固然子類也能夠加上本身的新的泛型類型

  • 注意點

    • 泛型類裏的泛型是以參數的形式存在的。

    • 能夠有多個參數:<T1,T2,T3>

    • 聲明構造器時

      • 空參構造器和普通的同樣 public MyGeneric() {}
      • 帶參構造器體如今參數上 public MyGeneric(String name, int age, T decs) {
    • 泛型不一樣的引用不能相互賦值

      ArrayList<String> list1 = new ArrayList<>();
      ArrayList<Integer> list2 = new ArrayList<>();
              
      list1 = list2;//這樣是錯誤的!
    • 泛型若是不指定,就會被擦除,泛型對應的類型按照Object處理,但不等價於Object。

    • 在類/接口上聲明的泛型,在本類或本接口中即表明某種類型,能夠做爲非靜態 屬性的類型、非靜態方法的參數類型、非靜態方法的返回值類型。但在靜態方法中不能使用類的泛型。(由於,靜態方法隨着類的加載而加載,而泛型是在實例化的時候指定,晚於靜態方法出生)

    • 異常類不能聲明爲泛型的。

    • 若是想在構造器中初始化一個T類型的數組,要寫做:

      T[] t = (T[])new Object[capacity]

二、泛型方法

  • 理解
    • 在方法中出現了泛型的結構,就是泛型方法。
    • 泛型方法中的泛型參數與類的泛型參數沒有關係。也就是說:泛型方法所屬的類是否是泛型無所謂。
    • 能夠聲明爲靜態的,由於泛型參數是在調用方法時肯定的,而不是來加載時。
  • 代碼實現一個泛型方法
public static <E> List<E> copyFromArrayToList(E[] arr){
    //這裏的第一個<E>是爲了避免讓編譯器把E當作是一個咱們自定義的類
    ArrayList<E> list = new ArrayList<>();
    
    for(E e : arr){
        list.add(e);
    }
    
    return list;
}

3、舉例泛型類和泛型方法的使用場景

一、泛型類舉例:

鏈接數據庫時,操做數據庫中的表的記錄

  • DAO類:定義了數據庫不一樣表的一些共性操做的類

    public class DAO<T> {}

  • CustomerDAO:定義了專門操做Customer表的類

    public class CustomerDAO extends DAO<Customer>{}

二、泛型方法舉例

//泛型方法舉例
    /*
    返回的東西是不肯定的,好比
        需求1:表中有多少條記錄
        需求2:獲取工資最大值
     */
    public <E> E getSomething(){
        return null;
    }

4、泛型在繼承上的體現

  • 首先,咱們知道在多態下,子類的對象能夠賦給父類的引用。
  • 可是,在泛型狀況下,再也不存在子父類的關係。例如,String是Object的子類,可是List<String >並非List<Object> 的子類。同理在方法參數中也同樣適用。這樣就致使一個問題,同一功能的方法要重載不少次。
  • 區別,類B是類A的子類,那麼B<T>仍然是A<T>的子類。

5、通配符

一、通配符

通配符用 ?來表示,含義爲:類A是類B的父類,G<A> G<B>並不存在什麼關係,兩者的共同父類是:G<?>。這樣就沒必要重載那麼多功能相同的方法了。

二、添加通配符後數據的寫入何人讀出

  • 寫入:對於List<?>,不能向其中添加數據,只能添加null;
  • 讀出:能夠讀取,讀取出的數據爲Object類型

三、有限制條件的通配符

  • 首先,通配符?能夠的範圍能夠理解爲: (無窮小,無窮大)
  • ? extends A的範圍就能夠理解爲:小於等於,即(無窮小,A]
  • ? super A的範圍就能夠理解爲:大於等於,即[A,無窮大)
相關文章
相關標籤/搜索