什么是JAVA泛型的原生類型(raw type),為什么不建議使用?
在我們寫代碼的時(shí)候,如果無意間寫出下面的代碼:
List names = new ArrayList();
...
在編譯時(shí),javac會(huì)提示:Raw use of parameterized class 'List',那么,什么是raw type呢?
raw type的歷史淵源
在很久很久以前,java還沒有泛型,如果要聲明一個(gè)名稱的列表,可能代碼是這樣的
// name is string
List names = new ArrayList();
使用這個(gè)列表時(shí),需要這樣使用:
names.add("張三");
...
for (int i = 0; i < names.size(); i++) {
String name = (String)names.get(i);
...
}
可以看到,每次取列表的值時(shí),都需要進(jìn)行一次強(qiáng)制類型轉(zhuǎn)換,非常繁瑣;而且在使用時(shí)還要時(shí)刻注意列表里面存儲(chǔ)的類型,使用起來很不方便。更嚴(yán)重的是,列表里面的內(nèi)容只能人工確保類型一致,很可能出現(xiàn)問題,比如下面這樣:
// 加了一個(gè)int進(jìn)去!
names.add(1);
在使用時(shí),代碼還是(String)names.get(i);,那么會(huì)在運(yùn)行時(shí)得到一個(gè)類型轉(zhuǎn)換錯(cuò)誤,導(dǎo)致線上BUG。
泛型橫空出世
為了解決上面的問題,java在1.5后增加了泛型,上面的代碼就可以改寫成這樣的:
List<String> names = new ArrayList<>();
names.add("xxx");
for (int i = 0; i < names.size(); i++) {
String name = names.get(i);
...
}
在編寫代碼的時(shí)候,聲明自己存儲(chǔ)的是字符串類型,編譯器就可以自動(dòng)完成強(qiáng)制類型轉(zhuǎn)換,不必再手動(dòng)轉(zhuǎn)換了。而且,當(dāng)我們嘗試將一個(gè)其他類型的值放到列表中時(shí),編譯器會(huì)直接報(bào)錯(cuò),如下所示:
names.add(1);
// 'add(java.lang.String)' in 'java.util.List' cannot be applied to '(int)'
編譯器會(huì)幫助我們做類型檢查,防止錯(cuò)誤類型進(jìn)入。
什么是raw type,為什么不建議使用
現(xiàn)在可以回答什么是raw type了。raw type就是定義了泛型的類中,不帶任何實(shí)際類型參數(shù)的那種類型。比如List<E>的類型參數(shù)是E,那么不帶類型參數(shù)的List就是原生類型了。
由此可見,原生類型是一個(gè)歷史遺留的問題,他的存在是為了兼容沒有泛型時(shí)的老代碼。
使用泛型,可以獲得安全性(類型檢查)和描述性(類型參數(shù))的優(yōu)勢(shì),而使用原生類型則已經(jīng)沒有任何優(yōu)勢(shì)。
List<Object>,List<?>與原生類型的區(qū)別
實(shí)踐中,我們也會(huì)遇到List<Object>和List<?>這兩種看起來和原生類型很像的,特別是List<?>,那么,他們和原生類型有什么區(qū)別呢?
List<Object>與原生類型
List<Object>是一個(gè)參數(shù)化的List,他的類型參數(shù)為Object,相當(dāng)于明確告知編譯器這個(gè)列表可以持有任意類型的對(duì)象,使用這個(gè)仍然是類型安全的,和其他的類型參數(shù)相比,沒有明顯區(qū)別。
但是,如果我們有一個(gè)List<String>,想把它賦值給List<Object>,是不合法的,因?yàn)樗麄z不是同一類型。將List<Object>聲明為List<? extends Object>,才可以。如下所示:
List<String> names = new ArrayList<>();
List<Object> objects;
// 非法:Incompatible types
// objects = names;
List<? extends Object> objectExtends;
// 合法
objectExtends = names;
List<?>與原生類型
List<?>中的?是通配符,通配符可以被? extends或者? super來限制,如果沒有添加任何限制,則是無限制的通配符,說明這個(gè)參數(shù)可以是任意集合。使用List<?>是類型安全的,因?yàn)樗豢梢詫⒊薾ull以外的任何值放到集合中(原生類型List則可以將任何值放到集合中,導(dǎo)致類型不安全。)
使用raw type的例外場(chǎng)景
除非迫不得已,不要使用原生類型。下面的情形就是迫不得已的時(shí)候,必須要使用原生態(tài)類型了。
在獲取帶泛型的類的class對(duì)象時(shí),必須使用原生類型。就是寫成List.class是合法的,而List<String>.class就是非法的。