泛型的使用及相關知識點

2020-10-19 11:01:24

泛型的概念

      所謂泛型,就是允許在定義類、介面時通過一個標識表示類中某個屬性的型別或者是某個方法的返回值及引數型別。這個型別引數將在使用時(例如,繼承或實現這個介面,用這個型別宣告變數、建立物件時)確定(即傳入實際的型別引數,也稱為型別實參)。

使用泛型的好處

  1. 解決元素儲存的安全性問題,好比商品、藥品標籤,不會弄錯。
  2. 解決獲取資料元素時,需要型別強制轉換的問題,好比不用每回拿商品、藥品都要辨別。
  3. Java泛型可以保證如果程式在編譯時沒有發出警告,執行時就不會產生ClassCastException異常。同時,程式碼更加簡潔、健壯。

泛型的使用

泛型的定義

class Person<T> {
// 使用T型別定義變數
private T info;
// 使用T型別定義一般方法
public T getInfo() {
return info; 
}
public void setInfo(T info) {
this.info = info; 
}
// 使用T型別定義構造器
public Person() {
}
public Person(T info) {
this.info = info; 
}
// static的方法中不能宣告泛型
//public static void show(T t) {
//
//}
// 不能在try-catch中使用泛型定義
//public void test() {
//try {
//
//} catch (MyException<T> ex) {
//
//}
//}
}

自定義泛型結構

自定義泛型類、泛型介面

  1. 泛型類可能有多個引數,此時應將多個引數一起放在尖括號內。比如:
    <E1,E2,E3>
  2. 泛型類的構造器如下:public GenericClass(){}。
    而下面是錯誤的:public GenericClass(){}
  3. 範例化後,操作原來泛型位置的結構必須與指定的泛型型別一致。
  4. 泛型不同的參照不能相互賦值。
    儘管在編譯時ArrayList和ArrayList是兩種型別,但是,在執行時只有一個ArrayList被載入到JVM中。
  5. 泛型如果不指定,將被擦除,泛型對應的型別均按照Object處理,但不等價於Object。
  6. 經驗:泛型要使用一路都用。要不用,一路都不要用。
  7. 如果泛型結構是一個介面或抽象類,則不可建立泛型類的物件。
  8. jdk1.7,泛型的簡化操作:ArrayList flist = new ArrayList<>();
  9. 泛型的指定中不能使用基本資料型別,可以使用包裝類替換。
  10. 在類/介面上宣告的泛型,在本類或本介面中即代表某種型別,可以作為非靜態屬性的型別、非靜態方法的引數型別、非靜態方法的返回值型別。但在靜態方法中不能使用類的泛型。
  11. 異常類是不能用泛型的
  12. 不能使用new E[]。但是可以:E[] elements = (E[])new Object[capacity];
    參考:ArrayList原始碼中宣告:Object[] elementData,而非泛型引數型別陣列.
  13. 父類別有泛型,子類可以選擇保留泛型也可以選擇指定泛型型別:
    ● 子類不保留父類別的泛型:按需實現
          → 沒有型別 擦除
          → 具體型別
    ● 子類保留父類別的泛型:泛型子類
          → 全部保留
          → 部分保留
    結論:子類必須是「富二代」,子類除了指定或保留父類別的泛型,還可以增加自己的泛型
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> {
}
class Father<T1, T2> {
}
// 子類不保留父類別的泛型
// 1)沒有型別 擦除
class Son<A, B> extends Father{//等價於class Son extends Father<Object,Object>{
}
// 2)具體型別
class Son2<A, B> extends Father<Integer, String> {
}
// 子類保留父類別的泛型
// 1)全部保留
class Son3<T1, T2, A, B> extends Father<T1, T2> {
}
// 2)部分保留
class Son4<T2, A, B> extends Father<Integer, T2> {
}

自定義泛型方法

      方法,也可以被泛型化,不管此時定義在其中的類是不是泛型類。在泛型方法中可以定義泛型引數,此時,引數的型別就是傳入資料的型別。

泛型方法的格式:[存取許可權] <泛型> 返回型別 方法名([泛型標識 引數名稱]) 丟擲的異常

public class DAO {
public <E> E get(int id, E e) {
        E result = null;
        return result; 
    } 
}

泛型在繼承上的體現

      如果B是A的一個子型別(子類或者子介面),而G是具有泛型宣告的類或介面,G< B >並不是G< A >的子型別!
比如:String是Object的子類,但是List< String >並不是List< Object >的子類。

public void testGenericAndSubClass() {
    Person[] persons = null;
	Man[] mans = null;
// 而 Person[] 是 Man[] 的父類別.
	persons = mans;
	Person p = mans[0];
// 在泛型的集合上
	List<Person> personList = null;
	List<Man> manList = null;
// personList = manList;(報錯) }

泛型中萬用字元的使用

1.使用型別萬用字元:?
比如:List<?> ,Map<?,?>
List<?>是List、List等各種泛型List的父類別。
2.讀取List<?>的物件list中的元素時,永遠是安全的,因為不管list的真實型別是什麼,它包含的都是Object。
3.寫入list中的元素時,不行。因為我們不知道c的元素型別,我們不能向其中新增物件。
■ 唯一的例外是null,它是所有型別的成員

● <?>
允許所有泛型的參照呼叫

● 萬用字元指定上限
上限extends:使用時指定的型別必須是繼承某個類,或者實現某個介面,即<=

● 萬用字元指定下限
下限super:使用時指定的型別不能小於操作的類,即>=
● 舉例:
      → <? extends Number> (無窮小 , Number]
      只允許泛型為Number及Number子類的參照呼叫

      → <? super Number> [Number , 無窮大)
      只允許泛型為Number及Number父類別的參照呼叫

      → <? extends Comparable>
      只允許泛型為實現Comparable介面的實現類的參照呼叫

public static void printCollection3(Collection<? extends Person> coll) {
//Iterator只能用Iterator<?>或Iterator<? extends Person>.why?
	Iterator<?> iterator = coll.iterator();
	while (iterator.hasNext()) {
		System.out.println(iterator.next());
} }
public static void printCollection4(Collection<? super Person> coll) {
//Iterator只能用Iterator<?>或Iterator<? super Person>.why?
	Iterator<?> iterator = coll.iterator();
	while (iterator.hasNext()) {
		System.out.println(iterator.next());
} }