1. 程式人生 > 其它 >關於List、List<?>、List<Object>的區別

關於List、List<?>、List<Object>的區別

關於List、List<?>、List的區別

定義:宣告中具有一個或者多個型別引數(type parameter)的類或者介面,就是泛型類或者介面。泛型類和介面統稱為泛型(generic type)

每種泛型定義一組型別形參(formal type parameters),這些型別形參有時也被簡稱為型別引數(type parameter),例如對於泛型(generic type)List而言,List就是一個引數化的型別(parameterized type),String就是對應於型別形參****(formal type parameters)型別實參(actual type parameter)

每個泛型定義一個原生型別(raw type),即不帶任何型別引數的型別名稱,例如,與List對應的原生型別是List。原生型別就像從型別宣告中刪除了所有泛型資訊一樣。實際上原生型別List與Java平臺在有泛型之前的介面型別List完全一樣。

容器類使用泛型的好處:

  • 安全性:在對引數化型別的容器中放入了錯誤即不匹配的型別的時候,編譯器將會強制性進行錯誤提示。
  • 便利性:當從容器中取出元素的時候不用自己手動將Object轉換為元素的實際型別了,編譯器將隱式地進行自動轉換。
  • 表述性:帶有型別實參的泛型即引數化型別,可以讓人看到實參就知道里面的元素E都是什麼型別。

所以,不應該使用原生型別的原因如下:

雖然使用原生型別是合法的,但不提倡這樣做,因為如果使用原生型別,就失掉了泛型在安全性和表述性方面的所有優勢;安全性:比如我們可能會不小心把一個java.util.Date例項錯誤地放進一個原本包含java.sql.Date例項的集合當中,雖然在編譯期不會出現任何錯誤,但在執行期一旦嘗試型別轉換就會發生ClassCastException,而泛型原本就是為了避免這種問題而出現的;表述性:不像帶有型別實參的泛型即引數化型別那樣,讓人看到實參就知道里面的元素E都是什麼型別。

泛型的子型別化的原則:List型別是原生型別List的一個子型別,而不是引數化型別List的子型別

List、List<?>、List的區別
  • List,即原始型別,其引用變數可以接受任何對應List的引數化型別, 包括List<?>,並且可以新增任意型別的元素。但其缺點在於不安全性、不便利性、不表述性(***不應該使用原生型別的原因**
    *)。
  • List<?>,即萬用字元型別,其引用變數,同樣可以接受任何對應List的引數化型別,包括List,但不能新增任何元素,保證了安全性和表述性。但不具有表述性,從中取出的元素時Object型別,要通過手動轉換才能得到原本的型別。
  • List,即實際型別引數為Object的引數化型別,其引用變數可以接受List,可以新增元素,但不能接受除了其本身外的任何引數化型別(泛型的子型別化原則)。
引用變數的型別 名稱 可以接受的型別 能否新增元素 安全性 便利性 表述性
List 原始型別 任何對應List的引數化型別, 包括List<?> 可以新增任意型別的元素
List<?> 萬用字元型別 以接受任何對應List的引數化型別,包括List 不能新增任何元素
List 實際型別引數為Object的引數化型別 僅可以接受List和其本身型別 可以新增任意型別元素
  • List
List list = new ArrayList();
list.add(user);
list.add(user2);
// 僅可以接受List(List沒有跟隨具體的物件)和其本身型別List<Object>
public void EntityList(List<Object> list) {}
  • List<?>
List<User> list = new ArrayList();
list.add(user);
list.add(user2);

public void EntityList(List<?> list) {}
// 結果需要手動轉回,所以沒有便利性
list = (List<User>)list;

可以看到相比引數化型別的List,List<?>缺點在於不能新增任何元素並且不具有便利性,如果這無法滿足功能要求可以考慮使用泛型方法和有邊界的萬用字元。

根據The Java™ Tutorials,原生型別物件可以被賦給引數化型別(包括有界無界萬用字元引數化型別),同樣,引數化型別(包括有界無界萬用字元引數化型別)物件也能被賦給原生型別