什么是JAVA泛型的原生類型(raw type),為什么不建議使用?

什么是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就是非法的。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容