“嗨,阿米戈!”

“嗨,艾莉!”

“今天,Rishi 和我將向大家介紹仿製藥。”

“等等,我想我幾乎什麼都知道了。”

“幾乎所有,但不是所有。”

“真的嗎?好吧,我準備好傾聽了。”

“那我們開始吧。”

“在 Java 中,泛型是具有類型參數的類。”

“至於為什麼要發明泛型,看代碼中的註釋:”

例子
ArrayList stringList = new ArrayList();
stringList.add("abc"); // Add a string to the list
stringList.add("abc"); // Add a string to the list
stringList.add( 1 ); // Add a number to the list

for(Object o: stringList)
{
 String s = (String) o; // There will be an exception here when we get to the integer
}

如何使用泛型解決問題:

例子
ArrayList<String> stringList = new ArrayList<String>();
stringList.add("abc"); // Add a string to the list
stringList.add("abc"); // Add a string to the list
stringList.add( 1 ); // There will be a compilation error here

for(Object o: stringList)
{
 String s = (String) o;
}

“這段代碼根本無法編譯,並且在編譯期間會注意到由於添加錯誤的數據類型而導致的錯誤。”

“是的,我已經知道了。”

“好,好。重複是學習之母。”

“但是 Java 的創建者在創建泛型時有點懶惰。他們沒有製作帶有參數的完整類型,而是進行了巧妙的優化。實際上,他們沒有向泛型添加任何有關類型參數的信息。相反,所有的編譯過程中會發生魔法。”

泛型代碼
List<String> strings = new ArrayList<String>();
strings.add("abc");
strings.add("abc");
strings.add( 1); // Compilation error

for(String s: strings)
{
 System.out.println(s);
}
到底發生了什麼
List strings = new ArrayList();

strings.add((String)"abc");
strings.add((String)"abc");
strings.add((String) 1); // Compilation error

for(String s: strings)
{
 System.out.println(s);
}

“那很光滑。”

“是的,但是這種方法有一個副作用。 泛型類內部不存儲類型參數的信息。 這種方法後來被稱為類型擦除。”

“換句話說,如果你有自己的帶有類型參數的類,你就不能在類中使用關於它們的信息。”

泛型代碼
class Zoo<T>
{
 ArrayList<T> pets = new ArrayList<T>();

 public T createAnimal()
 {
  T animal = new T();
  pets.add(animal)
  return animal;
 }
}
到底發生了什麼
class Zoo
{
 ArrayList pets = new ArrayList();

 public Object createAnimal()
 {
  Object animal = new ???();
  pets.add(animal)
  return animal;
 }
}

“在編譯期間,所有參數類型都被替換為 Object。並且在類內部沒有關於傳遞給它的類型的信息。”

“是的,我同意,那不是最好的。”

“沒那麼可怕,我稍後會告訴你如何解決這個問題。”

但還有更多。Java 允許您為類型參數指定父類型。extends 關鍵字用於此。例如:

泛型代碼
class Zoo<T extends Cat>
{
 T cat;

 T getCat()
 {
  return cat;
 }

 void setCat (T cat)
 {
  this.cat = cat;
 }

 String getCatName()
 {
  return this.cat.getName();
 }
}
到底發生了什麼
class Zoo
{
 Cat cat;

 Cat getCat()
 {
  return cat;
 }

 void setCat(Cat cat)
 {
  this.cat = cat;
 }

 String getCatName()
 {
  return this.cat.getName();
 }
}

“注意兩個事實:”

“首先,你不能只傳遞任何類型作為參數——你只能傳遞 Cat 或繼承 Cat 的類。”

“其次,在 Zoo 類內部,類型 T 的變量現在可以調用 Cat 類的方法。 右側的列解釋了原因(因為 Cat 將在任何有 T 的地方被替換)”

“是的。如果我們說 Cat 或 Cat 的子類作為類型參數傳遞,那麼我們可以肯定類型 T 將始終具有 Cat 類的方法。”

“嗯,這很聰明。”