Java泛型

  • 2021 年 1 月 30 日
  • 筆記

你要做一個不動聲色的大人了。不準情緒化,不準偷偷想念,不準回頭看。去過自己另外的生活。你要聽話,不是所有的魚都會生活在同一片海里。 ——村上春樹 《舞!舞!舞!》

Q:什麼是泛型?

  • Java泛型本質:參數化類型

    從字面意思就可以看出,就是把「類型變成了參數」,如String類型、Integer類型等都作為參數。

==============================================================================

  • 泛型-承擔「保安」的角色

  • Q:泛型如何使用?

  1.List mylist1=new ArrayList();//未使用泛型;
  2.List<String> mylist2=new ArrayList<String>();//參數化類型;
  • 集合類容納的對象都是Object類的實例。也就是說字元串或整型數據等都能存入List列表中,因為Object類是所有類的父類。

=============================================================================

下面看看未使用泛型和使用了泛型的區別:

  • 未使用泛型:

  • 字元串數據、整型數據等都能存入List列表中,List列表把這些數據都看作時Object類型,當我們只想取出某種類型

    數據時,需要進行強制轉換。如上圖,字元串強轉字元串這個沒啥問題,但是整型強轉字元串類型就會運行報錯,出現

    ClassCastException錯誤(類型不匹配)。

=============================================================================

  • 使用泛型:

如上述程式碼,指定了傳入的數據類型必須是String類型,就像保安檢查一樣,是String類型的數據才能進來(存入),

如果不是就會被攔截(報錯)。把String作為參數,類型參數化,這就是泛型。當然你可以把其他類型作為參數。

=============================================================================

Q:泛型有什麼作用?

類型參數——類型實參與類型形參

  • 類型參數可以類比Java方法中的形參和實參,類型實參就如我前面舉的例子,那類型形參長什麼樣呢?

  • 看下圖:API文檔中List介面的定義

  • 這很顯然使用了泛型,但它不是確定的數據類型,這就是類型形參。泛型中使用大寫的英文字母來作為類型形參。

  • 這裡的E是一個類型形參,實際中你可以寫成String、Integer等等。

=============================================================================

菱形運算符

List<String> mylist2=new ArrayList<String>();
  • 上述程式碼中,mylist2是List類型的,顯然創建的對象也必須是List類型的,任何其他類型的參數都會產生編譯錯誤。

    Java7增加了一種新的語言特性,稱為菱形運算符,上面程式碼可改寫為:

List<String> mylist2=new ArrayList<>();

泛型通配符

  1. 無界通配符
  2. 上界通配符
  3. 下界通配符

1.程式碼示例:PrintList方法列印任何類型的列表

public static void PrintList(List<Object> list) {
    for (Object o: list)//增強型for循環列印列表
        System.out.println(o + " ");
}
//The method PrintList(List<Object>) in the type PrintList is not applicable for the arguments (List<Integer>)
//不能列印List<Integer>, List<String>等等,因為它們不是List<Object>的子類型(只存儲整型的列表不是可存儲任何類型的列表的子類)。
//使用通配符(?):表示未知類型,即可以是String類型,也可以是Integer類型等等。

public static void PrintList(List<?> list){}

2/3.程式碼示例:

class Food {}//食物類
class Fruit extends Food {}//水果類
class Apple extends Fruit {}//蘋果類
public class Plate<T> {//盤子類(泛型類)
      public Plate(T t){item=t;}
      public void set(T t){item=t;}//放和取操作
      public T get(){return item;}
      private T item;
      public static void main(String[] args) {
          //定義水果盤子
            Plate<Fruit> p1=new Plate<Apple>(new Apple());//Error 🍎與水果有繼承關係,但🍎盤子與水果盤子之間沒繼承關係
          //<? extends T>上界通配符,中間表示T及任何T的派生類的類型數據,這個例子中理解為『這個盤子能裝任何水果』,蘋果當然沒問題
          //能裝任何水果的盤子與蘋果盤子之間就有了繼承關係(Plate<? extends Fruit> 是Plate<Apple>的基類)
            Plate<? extends Fruit> p1=new Plate<Apple>(new Apple());
          //<? extends T>不能往裡存,只能往外取。是水果就能放進來,但編譯器並不知道是什麼水果,當要對蘋果賦值時,編譯器找不到。
            p1.set(new Fruit());  //Error
          //讀取出來的東西只能存放在Fruit或它的基類里。從水果盤子里來的肯定是水果,或者是食物(基類)
            Fruit newFruit1=p1.get();
            Object newFruit2=p1.get();
            Apple newFruit3=p1.get(); //Error
          //<? super Fruit>下界通配符,表示T及任何T的基類的類型數據,這個例子中理解為『這個盤子能裝任何食物(水果基類)』。
            Plate<? super Fruit> p2=new Plate<Fruit>(new Fruit());
          //<? super T>不影響往裡存,但往外取只能放在Object對象里
	    p2.set(new Fruit());
	    p2.set(new Apple());
	    Apple newFruit4=p2.get();    //Error
            Fruit newFruit5=p2.get();    //Error
            Object newFruit6=p2.get();
	}
}

上界下界參考> //blog.csdn.net/hello_worldee/article/details/77934244
簡單泛型類和介面

public class GenericMemoryCell <E> {//泛型類
	public E read() 
	  {return Value;}
	public void write(E x) 
	  {Value=x;}
	private E Value;
}
package java.lang
//泛型介面,Comparable介面;
public interface Comparable<E>
{
      public int compareTo(E other)
}
註:參考百度百科及《數據結構與演算法分析》
Tags: