泛型的意義和作用是啥?

hexter發表於2017-06-03

  簡單的說,意義和作用有:

  型別的引數化,就是可以把型別像方法的引數那樣傳遞。這一點意義非凡。

  泛型使編譯器可以在編譯期間對型別進行檢查以提高型別安全,減少執行時由於物件型別不匹配引發的異常。

  泛型方法,演算法的複用。蠻神奇的。

  想要理解為什麼引入泛型,就要知道沒有泛型的麻煩。

  我們知道現在的程式開發都是物件導向了,所以程式裡會有很多各種型別的物件,物件多了肯定需要有某種型別的容器來裝。所以就有了一些容器型別,比如陣列、ArrayList、HashMap、TreeSet等。

  對於陣列,我們知道需要在使用時指定陣列裝的物件型別,如:

Animal animal[];

Dog dog[];

  而對於集合型別容器如ArrayList、HashMap、TreeSet等,它們不但是容器,還提供了一些方法對容器內物件的操作方法,如get,set,sort。這個時候就需要知道容器內放的是什麼型別的物件,才能return或set。

  正因為程式開發人員可能把任何型別的物件放進集合容器,所以這些容器在設計的時候只能預設設計成裝Object型別物件。因為Java裡Object是根類。

  所以容器就成了類似這個樣子:

public class ListContainer {

private Object obj;

public Object getObj() {

return obj;

}

public void setObj(Object obj) {

this.obj = obj;

}

}

  這樣的話,根據多型,容器就能裝任何型別的物件了。不過,取出物件時則需求進行強制型別轉換,轉換成實際的型別。但這樣會有很多型別不安全問題,為什麼呢?因為編譯器沒法幫忙做型別檢查,導致程式碼在執行時易於出現ClassCastException異常。因此,型別安全問題只能由程式設計師自己來把關了,記住各種型別,避免型別轉換錯誤。

ListContainer myContainer = new ListContainer();

myContainer.setObj("123");

ArrayList objectList = new ArrayList();

objectList.add(myContainer);

//下面這句編譯時無異常,執行時會ClassCastException異常

Integer myStr = (Integer) ((ListContainer)objectList.get(0)).getObj();

//下面這句ok

String myStr = (String) ((ListContainer)objectList.get(0)).getObj();

  泛型出場:型別的引數化

  利用泛型,重新設計:

public class ListContainer<T> {

private T t;

public T getObj() {

return t;

}

public void setObj(T t) {

this.t= t;

}

}

  這裡<T>的T的型別的引數,具體T指代什麼型別,是String還是Animal還是Dog型別此處不管,而在程式設計師開發時使用到ListContainer時再指定,如:

ListContainer<Dog> myCon=new ListContainer<Dog>();

  這種環境下,編譯器就知道ListContainer容器是放Dog型別物件的。並進行型別安全檢查。

myCon.setObj(new Dog())//ok

myCon.setObj(“123”);//編譯時提醒型別錯誤

  這樣設計的容器在使用時編譯器就可以幫忙做很大一部分的型別安全檢查工作了,這就避免了很多執行時的ClassCastException異常,程式設計師也無需記住各種物件的型別和擔心型別匹配問題了。同時大部分情況下也不用做型別強制轉換工作了。

ListContainer<String> myContainer = new ListContainer<String>();

myContainer.setObj("123");

myContainer.setObj(new Dog());//編譯器就提醒型別異常

ArrayList objectList<ListContainer> = new ArrayList<ListContainer>();

objectList.add(myContainer);

Integer myStr = (objectList.get(0)).getObj(); //編譯時提醒型別異常

String myStr = (objectList.get(0)).getObj();

  當然泛型的<>裡也可以放多個引數,如:

public class MultiContainer<T,S> {

private T t;

private S s;

...

MultiContainer<String,Dog> multicon=new MultiContainer<String,Dog>();

  有界泛型

  看看這個泛型和多型的問題,Dog,Cat是Animal的子類:

public void killAll(ArrayList<Animal> animals){...};//Animal容器

...

ArrayList<Animal> animals=new ArrayList<Animal>();

animals.add(new Dog());

animals.add(new Cat());

killAll(animals);//這裡ok

ArrayList<Dog> dogs=new ArrayList<Dog>();//Dog是Animal的子類

dogs.add(new Dog());

dogs.add(new Dog());

killAll(dogs);//這裡編譯不通過

  在這裡看上去似乎多型不行了。

  這裡就要用到有界泛型:

  在使用泛型時,我們會有這種需求:需要指定泛型的型別範圍。有界型別就是在型別引數部分指定extends或super關鍵字,這裡的extends也含有implements的功能,分別用上限或下限來限制型別範圍,從而限制泛型的型別邊界。例如:

<T extends Animal>//限定T是Animal的子類

<T super Dog >//限定T是Dog的超類

  那麼上面那個多型問題就變成:

public void killAll(ArrayList<T extends Animal> animals){...};

  解決了。

<T extends Object&Comparable&Serializable>

  多個限定時我們可以使用&來進行分割,這時關鍵詞只能使用extends。與多重繼承類似,這裡只有一個類其他都是介面。

  泛型方法

  有時,我們設計的方法可能其引數型別是不限定的。這種場景如果用過載方法的方式來做的話,演算法重複,不是最好的方案。此時泛型方法就可以解決此類問題。

  如Calculator的add方法:

public static <N extends Number> double add(N a, N b){

double sum = 0;

sum = a.doubleValue() + b.doubleValue();

return sum;

}

  如果用過載來做的話,要很多重複程式碼了。

相關文章