制作一個(gè)網(wǎng)站怎么做北京優(yōu)化推廣
既然說(shuō)類型變量會(huì)在編譯的時(shí)候擦除掉,那為什么我們往 ArrayList 創(chuàng)建的對(duì)象中添加整數(shù)會(huì)報(bào)錯(cuò)呢?不是說(shuō)泛型變量String會(huì)在編譯的時(shí)候變?yōu)镺bject類型嗎?為什么不能存別的類型呢?既然類型擦除了,如何保證我們只能使用泛型變量限定的類型呢?
Java編譯器是通過(guò)先檢查代碼中泛型的類型,然后在進(jìn)行類型擦除,再進(jìn)行編譯。?
例如:
public static void main(String[] args) { ArrayList<String> list = new ArrayList<String>(); list.add("123"); list.add(123);//編譯錯(cuò)誤
}
在上面的程序中,使用add方法添加一個(gè)整型,在IDE中,直接會(huì)報(bào)錯(cuò),說(shuō)明這就是在編譯之前的檢查,因?yàn)槿绻窃诰幾g之后檢查,類型擦除后,原始類型為Object,是應(yīng)該允許任意引用類型添加的。可實(shí)際上卻不是這樣的,這恰恰說(shuō)明了關(guān)于泛型變量的使用,是會(huì)在編譯之前檢查的。
那么,這個(gè)類型檢查是針對(duì)誰(shuí)的呢?我們先看看參數(shù)化類型和原始類型的兼容。
以 ArrayList舉例子,以前的寫法:
ArrayList list = new ArrayList();
現(xiàn)在的寫法:
ArrayList<String> list = new ArrayList<String>();
如果是與以前的代碼兼容,各種引用傳值之間,必然會(huì)出現(xiàn)如下的情況:
ArrayList<String> list1 = new ArrayList(); //第一種 情況
ArrayList list2 = new ArrayList<String>(); //第二種 情況
這樣是沒有錯(cuò)誤的,不過(guò)會(huì)有個(gè)編譯時(shí)警告。
不過(guò)在第一種情況,可以實(shí)現(xiàn)與完全使用泛型參數(shù)一樣的效果,第二種則沒有效果。
因?yàn)轭愋蜋z查就是編譯時(shí)完成的,new ArrayList()只是在內(nèi)存中開辟了一個(gè)存儲(chǔ)空間,可以存儲(chǔ)任何類型對(duì)象,而真正涉及類型檢查的是它的引用,因?yàn)槲覀兪鞘褂盟胠ist1來(lái)調(diào)用它的方法,比如說(shuō)調(diào)用add方法,所以list1引用能完成泛型類型的檢查。而引用list2沒有使用泛型,所以不行。
舉例子:
public class Test { public static void main(String[] args) { ArrayList<String> list1 = new ArrayList(); list1.add("1"); //編譯通過(guò) list1.add(1); //編譯錯(cuò)誤 String str1 = list1.get(0); //返回類型就是String ArrayList list2 = new ArrayList<String>(); list2.add("1"); //編譯通過(guò) list2.add(1); //編譯通過(guò) Object object = list2.get(0); //返回類型就是Object new ArrayList<String>().add("11"); //編譯通過(guò) new ArrayList<String>().add(22); //編譯錯(cuò)誤 String str2 = new ArrayList<String>().get(0); //返回類型就是String }
}
通過(guò)上面的例子,我們可以明白,類型檢查就是針對(duì)引用的,誰(shuí)是一個(gè)引用,用這個(gè)引用調(diào)用泛型方法,就會(huì)對(duì)這個(gè)引用調(diào)用的方法進(jìn)行類型檢測(cè),而無(wú)關(guān)它真正引用的對(duì)象。
泛型中參數(shù)話類型為什么不考慮繼承關(guān)系?
在Java中,像下面形式的引用傳遞是不允許的:
ArrayList<String> list1 = new ArrayList<Object>(); //編譯錯(cuò)誤
ArrayList<Object> list2 = new ArrayList<String>(); //編譯錯(cuò)誤
我們先看第一種情況,將第一種情況拓展成下面的形式:
ArrayList<Object> list1 = new ArrayList<Object>();
list1.add(new Object());
list1.add(new Object());
ArrayList<String> list2 = list1; //編譯錯(cuò)誤
?實(shí)際上,在第4行代碼的時(shí)候,就會(huì)有編譯錯(cuò)誤。那么,我們先假設(shè)它編譯沒錯(cuò)。那么當(dāng)我們使用list2引用用get()方法取值的時(shí)候,返回的都是String類型的對(duì)象(上面提到了,類型檢測(cè)是根據(jù)引用來(lái)決定的),可是它里面實(shí)際上已經(jīng)被我們存放了Object類型的對(duì)象,這樣就會(huì)有ClassCastException
了。所以為了避免這種極易出現(xiàn)的錯(cuò)誤,Java不允許進(jìn)行這樣的引用傳遞。(這也是泛型出現(xiàn)的原因,就是為了解決類型轉(zhuǎn)換的問(wèn)題,我們不能違背它的初衷)。
再看第二種情況,將第二種情況拓展成下面的形式:
ArrayList<String> list1 = new ArrayList<String>();
list1.add(new String());
list1.add(new String());ArrayList<Object> list2 = list1; //編譯錯(cuò)誤
沒錯(cuò),這樣的情況比第一種情況好的多,最起碼,在我們用list2取值的時(shí)候不會(huì)出現(xiàn)ClassCastException,因?yàn)槭菑腟tring轉(zhuǎn)換為Object。可是,這樣做有什么意義呢,泛型出現(xiàn)的原因,就是為了解決類型轉(zhuǎn)換的問(wèn)題。
我們使用了泛型,到頭來(lái),還是要自己強(qiáng)轉(zhuǎn),違背了泛型設(shè)計(jì)的初衷。所以java不允許這么干。再說(shuō),你如果又用list2往里面add()新的對(duì)象,那么到時(shí)候取得時(shí)候,我怎么知道我取出來(lái)的到底是String類型的,還是Object類型的呢?
所以,要格外注意,泛型中的引用傳遞的問(wèn)題。