什么是泛型?
泛型是程序設(shè)計(jì)語言的一種特性邑遏。允許程序員在強(qiáng)類型程序設(shè)計(jì)語言中編寫代碼時(shí)定義一些可變部分忠售,那些部分在使用前必須作出指明。各種程序設(shè)計(jì)語言和其編譯器仿便、運(yùn)行環(huán)境對(duì)泛型的支持均不一樣体啰。將類型參數(shù)化以達(dá)到代碼復(fù)用提高軟件開發(fā)工作效率的一種數(shù)據(jù)類型。泛型類是引用類型嗽仪,是堆對(duì)象荒勇,主要是引入了類型參數(shù)這個(gè)概念。
泛型的用處闻坚?
一些強(qiáng)類型編程語言支持泛型沽翔,其主要目的是加強(qiáng)類型安全及減少類轉(zhuǎn)換的次數(shù),但一些支持泛型的編程語言只能達(dá)到部分目的。
Java早期是使用Object類型來接收任意的對(duì)象類型仅偎。但是在實(shí)際的運(yùn)用中會(huì)有類型轉(zhuǎn)換的問題跨蟹,即:向上轉(zhuǎn)型沒有任何問題,但是向下轉(zhuǎn)型時(shí)其實(shí)隱含了類型轉(zhuǎn)換的問題橘沥,這就是程序不安全的原因窗轩,也就是因?yàn)榇嬖谶@種安全隱患,所以Java在JDK5以后就提供了泛型來解決這個(gè)安全問題座咆。
使用泛型定義的可以防止在運(yùn)行時(shí)強(qiáng)轉(zhuǎn)類型導(dǎo)致出現(xiàn)ClassCastExecption痢艺,把所有的因?yàn)轭愋筒幻鞔_的問題都在編譯時(shí)指出。
有了泛型以后:
1介陶、代碼更加簡潔【不用強(qiáng)制轉(zhuǎn)換】
2堤舒、程序更加健壯【只要編譯時(shí)期沒有警告,那么運(yùn)行時(shí)期就不會(huì)出現(xiàn)ClassCastException異辰锫】
3植酥、可讀性和穩(wěn)定性更強(qiáng)【在編寫集合的時(shí)候镀岛,就限定了類型】
泛型的分類
泛型類
- 泛型類就是把泛型定義在類上弦牡,創(chuàng)建類或使用類時(shí),需要提前把類型明確漂羊,這樣的話提前明確了類型表示該類就是什么類型驾锰。不用擔(dān)心強(qiáng)轉(zhuǎn)或者運(yùn)行時(shí)類型不安全問題。
泛型方法
- 定義在方法上表示使用方法時(shí)傳入或者取出的參數(shù)類型需要明確走越。
泛型接口
- 說白了就是泛型類椭豫,因?yàn)榻涌谛枰悂韺?shí)現(xiàn)才能使用。
泛型類派生的子類
- 分為明確類型的子類和不明確類型的子類旨指。
不明確類型的就需要在下一層的子類或者創(chuàng)建的實(shí)例類來明確類型 - 注意
重寫父類方法赏酥,返回值的類型要和父類一樣
類聲明的泛型只對(duì)非靜態(tài)成員有效
因?yàn)殪o態(tài)成員在dex裝載時(shí)就會(huì)把所有靜態(tài)修飾的字段全部初始化了,并不是運(yùn)行時(shí)再去明確類型谆构。
類型通配符
通配符以 裸扶?修飾的,表示未知類型搬素。
通配符可用作各種情況:作為參數(shù)呵晨,字段或局部變量的類型;有時(shí)也作為返回類型熬尺;通配符從不用作泛型方法調(diào)用摸屠、泛型類實(shí)例創(chuàng)建或超類型的類型參數(shù)。
為什么要用通配符粱哼?
例子:你要遍歷并打印外界傳入的一個(gè)List季二,但是你不知道這個(gè)List的類型是什么?你可以使用Object修飾泛型揭措,但是泛型中的<Object>并不是像以前那樣有繼承關(guān)系的戒傻,也就是說List<Object>和List<String>是毫無關(guān)系的税手!我們只能遍歷裝載object的集合。
所以Java泛型提供了類型通配符
需纳?號(hào)通配符可以表示可以匹配任意類型芦倒,但是使用?通配符只能調(diào)用和類型無關(guān)的方法不翩,不能調(diào)用于類型有關(guān)的方法兵扬。也就是說,在上面的List集合口蝠,我是不能使用add()方法的器钟。因?yàn)閍dd()方法是把對(duì)象丟進(jìn)集合中,而現(xiàn)在我是不知道對(duì)象的類型是什么妙蔗。
private void demo(List<?> list){
list.get(index);//這是沒問題的
list.add(任意元素);//提示:in List cannot be applied to XX類型
}
上界通配符
表示設(shè)定類型的上限傲霸,也就是說是明確他的類型最多是定義的類型自身和它的子類
例子:現(xiàn)在,我想接收一個(gè)List集合眉反,它只能操作數(shù)字類型的元素【Float昙啄、Integer、Double寸五、Byte等數(shù)字類型都行】梳凛,怎么做?梳杏?韧拒?
List<? extends Number>
這樣你就把元素類型給明確了,List裝載的對(duì)象只能是Number的子類或自身十性。
下界通配符
表示設(shè)定類型的下限叛溢,表示設(shè)定的類型只能是它自身或者它的父類
例子:
<? super Type>
那它有什么用呢?劲适?我們來想一下楷掉,當(dāng)我們想要?jiǎng)?chuàng)建一個(gè)TreeSet<String>類型的變量的時(shí)候,并傳入一個(gè)可以比較String大小的Comparator减响。
那么這個(gè)Comparator的選擇就有很多了靖诗,它可以是Comparator<String>,還可以是類型參數(shù)是String的父類支示,比如說Comparator<Objcet>....
這樣做刊橘,就非常靈活了。也就是說颂鸿,只要它能夠比較字符串大小促绵,就行了
PECS原則
如果從集合中讀取類型T的元素,但是不能寫入,可以使用? extends 通配符
如果從集合中寫入類型T的元素败晴,但是不能讀取浓冒,可以使用? super 通配符
如果既要讀也要寫,那么就不要使用任何通配符
例子:
? extends T
public class Test {
static class Food {}
static class Fruit extends Food {}
static class Apple extends Fruit {}
public static void main(String[] args) throws IOException {
List<? extends Fruit> fruits = new ArrayList<>();
fruits.add(new Food()); // compile error
fruits.add(new Fruit()); // compile error
fruits.add(new Apple()); // compile error
fruits = new ArrayList<Fruit>(); // compile success
fruits = new ArrayList<Apple>(); // compile success
fruits = new ArrayList<Food>(); // compile error
fruits = new ArrayList<? extends Fruit>(); // compile error: 通配符類型無法實(shí)例化
Fruit object = fruits.get(0); // compile success
}
}
存入數(shù)據(jù)
- 賦值是參數(shù)化類型為 Fruit 的集合和其子類的集合都可以成功尖坤,通配符類型無法實(shí)例化稳懒。
- 編譯器會(huì)阻止將任何類加入fruits。在向fruits中添加元素時(shí)慢味,編譯器會(huì)檢查類型是否符合要求场梆。因?yàn)榫幾g器只知道fruits是Fruit某個(gè)子類的List,但并不知道這個(gè)子類具體是什么類纯路,為了類型安全或油,只好阻止向其中加入任何子類。
- 那么可不可以加入Fruit呢驰唬?很遺憾顶岸,也不可以。事實(shí)上叫编,不能往使用了? extends的數(shù)據(jù)結(jié)構(gòu)里寫入任何的值辖佣。
讀取數(shù)據(jù)
但是,由于編譯器知道它總是Fruit的子類型宵溅,因此我們總可以從中讀取出Fruit對(duì)象:
Fruit fruit = fruits.get(0);
? super T
public class Test {
static class Food {}
static class Fruit extends Food {}
static class Apple extends Fruit {}
public static void main(String[] args) throws IOException {
List<? super Fruit> fruits = new ArrayList<>();
fruits.add(new Food()); // compile error
fruits.add(new Fruit()); // compile success
fruits.add(new Apple()); // compile success
fruits = new ArrayList<Fruit>(); // compile success
fruits = new ArrayList<Apple>(); // compile error
fruits = new ArrayList<Food>(); // compile success
fruits = new ArrayList<? super Fruit>(); // compile error: 通配符類型無法實(shí)例化
Fruit object = fruits.get(0); // compile error
}
}
存入數(shù)據(jù)
- super 通配符類型同樣不能實(shí)例化凌简,F(xiàn)ruit 和其超類的集合均可賦值
- 這里 add 時(shí) Fruit 及其子類均可成功上炎,為啥呢恃逻?因?yàn)橐阎?fruits 的參數(shù)化類型必定是 Fruit 或其超類 T,那么 Fruit 及其子類肯定可以賦值給 T藕施。
- 出于對(duì)類型安全的考慮寇损,我們可以加入Apple對(duì)象或者其任何子類(如RedApple)對(duì)象(因?yàn)榫幾g器會(huì)自動(dòng)向上轉(zhuǎn)型),但由于編譯器并不知道List的內(nèi)容究竟是Apple的哪個(gè)超類裳食,因此不允許加入特定的任何超類型矛市。
讀取數(shù)據(jù)
編譯器在不知道這個(gè)超類具體是什么類,只能返回Object對(duì)象诲祸,因?yàn)镺bject是任何Java類的最終祖先類浊吏。
Object fruit = fruits.get(0);
泛型方法和類型通配符的區(qū)別?
例子:
a. 類型通配符:void func(List<? extends A> list);
b. 完全可以用泛型方法完美解決:<T extends A> void func(List<T> list);
1)你會(huì)發(fā)現(xiàn)所有能用類型通配符(?)解決的問題都能用泛型方法解決救氯,并且泛型方法可以解決的更好找田。
上面兩種方法可以達(dá)到相同的效果(?可以代表范圍內(nèi)任意類型,而T也可以傳入范圍內(nèi)的任意類型實(shí)參)着憨,并且泛型方法更進(jìn)一步墩衙,?泛型對(duì)象是只讀的,而泛型方法里的泛型對(duì)象是可修改的,即List<T> list中的list是可修改的F岣摹心铃!
- 要說兩者最明顯的區(qū)別就是:
- ?泛型對(duì)象是只讀的,不可修改挫剑,因?yàn)?類型是不確定的去扣,可以代表范圍內(nèi)任意類型;
- 而泛型方法中的泛型參數(shù)對(duì)象是可修改的樊破,因?yàn)轭愋蛥?shù)T是確定的(在調(diào)用方法時(shí)確定)厅篓,因?yàn)門可以用范圍內(nèi)任意類型指定;
注意:前者是代表捶码,后者是指定羽氮,指定就是確定的意思,而代表卻不知道代表誰惫恼,可以代表范圍內(nèi)所有類型档押。
- 這樣好像說的通配符?一無是處,但是并不是這樣祈纯,Java設(shè)計(jì)類型通配符?是有道理的令宿,首先一個(gè)最明顯的優(yōu)點(diǎn)就是?的書寫要比泛型方法簡潔,無需先聲明類型參數(shù)腕窥,其次它們有各自的應(yīng)用場(chǎng)景:
- 一般只讀就用?粒没,要修改就用泛型方法,例如一個(gè)進(jìn)行修改的典型的泛型方法的
例子:
public <T> void func(List<T> list, T t) {
list.add(t);
}
- 在多個(gè)參數(shù)簇爆、返回值之間存在類型依賴關(guān)系就應(yīng)該使用泛型方法癞松,否則就應(yīng)該是通配符?:
具體講就是,如果一個(gè)方法的返回值入蛆、某些參數(shù)的類型依賴另一個(gè)參數(shù)的類型就應(yīng)該使用泛型方法响蓉,因?yàn)楸灰蕾嚨念愋腿绻遣淮_定的?,那么其他元素就無法依賴它)哨毁。
例如:
<T> void func(List<? extends T> list, T t);
即第一個(gè)參數(shù)依賴第二個(gè)參數(shù)的類型(第一個(gè)參數(shù)list的類型參數(shù)必須是第二個(gè)參數(shù)的類型或者其子類)枫甲;
可以看到,Java支持泛型方法和?混用;
這個(gè)方法也可以寫成:
<T, E extends T> void func(List<E> list, T t);
明顯意義是一樣的扼褪,只不過這個(gè)list可以修改想幻,而上一個(gè)list無法修改。
總之就是一旦返回值话浇、形參之間存在類型依賴關(guān)系就只能使用泛型方法脏毯;
否則就應(yīng)該使用? ;
- 對(duì)泛型方法的類型參數(shù)進(jìn)行規(guī)約:即有時(shí)候可能不必使用泛型方法的地方你不小心麻煩地寫成了泛型方法凳枝,而此時(shí)你可以將其規(guī)約成使用?的最簡形式
- 總結(jié)地來講就是一句話:只出現(xiàn)一次 & 對(duì)它沒有任何依賴
例如:
<T, E extends T> void func(List<T> l1, List<E> l2);
這里E只在形參中出現(xiàn)了一次(類型參數(shù)聲明不算)抄沮,并且沒有任何其他東西(方法形參跋核、返回值)依賴它,那么就可以把E規(guī)約成?
E崖颉砂代!最終規(guī)約的結(jié)果就是:
<T> void func(List<T> l1, List<? extends T> l2);
- 一個(gè)最典型的應(yīng)用就是容器賦值方法(Java的API):
public static <T> void Collections.copy(List<T> dest, List<? extends T> src) { ... }
!率挣!從src拷貝到dest刻伊,那么dest最好是src的類型或者其父類,因?yàn)檫@樣才能類型兼容椒功,并且src只是讀取捶箱,沒必要做修改,因此使用?還可以強(qiáng)制避免你對(duì)src做不必要的修改动漾,增加的安全性丁屎。
原則
- 如果參數(shù)之間的類型有依賴關(guān)系,或者返回值是與參數(shù)之間有依賴關(guān)系的旱眯。那么就使用泛型方法晨川。
- 如果沒有依賴關(guān)系的,就使用通配符删豺,通配符會(huì)靈活一些共虑。
kotlin 泛型協(xié)變和泛型約束
out T 與 in T
out T 等價(jià)于 ? extends T in T 等價(jià)于 ? super T 此外, 還有 * 等價(jià)于 ?
val list:ArrayList<out Number> = arrayListOf()
val lisat:ArrayList<in Number> = arrayListOf()
泛型約束
//表示T泛型參數(shù)約束為Number和其子類
private fun Demo(T: Number) {
val list: ArrayList<in Number> = arrayListOf()
list.add(T)
}
什么是泛型擦除
JDK5提出了泛型這個(gè)概念,但是JDK5以前是沒有泛型的呀页。也就是泛型是需要兼容JDK5以下的集合的妈拌。
Java中的泛型都是偽泛型,意思是在Java虛擬機(jī)中不存在泛型類型對(duì)象蓬蝶,所有對(duì)象都是普通類尘分。
泛型是提供給javac編譯器使用的,它用于限定集合的輸入類型疾党,讓編譯器在源代碼級(jí)別上音诫,即擋住向集合中插入非法數(shù)據(jù)惨奕。但編譯器編譯完帶有泛形的java程序后雪位,生成的class文件中將不再帶有泛形信息,以此使程序運(yùn)行效率不受到影響梨撞,這個(gè)過程稱之為“擦除”雹洗。
比如限定一個(gè)Animals類型,那么在虛擬機(jī)中所存的就是Animals類卧波,不存在其子類时肿。
無論何時(shí)定義一個(gè)泛型類型,都會(huì)自動(dòng)提供一個(gè)相應(yīng)的 原始類型 (raw type)(不存在泛型 )港粱。原始類型的名字就是刪去類型參數(shù)后泛型類的類型名,擦除類型變量螃成,并替換為 限定類型(沒有限定的變量就用 Object )旦签。
類擦除后如何保證類型限定符的類型?
Java 編譯器通過先檢查代碼中泛型的類型寸宏,然后再進(jìn)行類型擦除宁炫,再進(jìn)行編譯。
翻譯泛型表達(dá)式
當(dāng)程序調(diào)用泛型方法時(shí)氮凝,因?yàn)樘摂M機(jī)進(jìn)行了泛型擦除羔巢,所以這個(gè)時(shí)候獲取到的類型應(yīng)該是其類型上限,但是在虛擬機(jī)中會(huì)進(jìn)行強(qiáng)制轉(zhuǎn)換成對(duì)應(yīng)類型罩阵。
那么它是怎么知道什么時(shí)候轉(zhuǎn)換成哪個(gè)類型呢竿秆?
看下ArrayList.get()方法
public E get(int index) {
RangeCheck(index);
return (E) elementData[index];
}
可以看到,在return之前稿壁,會(huì)根據(jù)泛型變量進(jìn)行強(qiáng)轉(zhuǎn)幽钢。假設(shè)泛型類型變量為Date,雖然泛型信息會(huì)被擦除掉傅是,但是會(huì)將(E) elementData[index]搅吁,編譯為(Date)elementData[index]。所以我們不用自己進(jìn)行強(qiáng)轉(zhuǎn)落午。當(dāng)存取一個(gè)泛型域時(shí)也會(huì)自動(dòng)插入強(qiáng)制類型轉(zhuǎn)換谎懦。假設(shè)Pair類的value域是public的,那么表達(dá)式:
Date date = pair.value;
類型擦除與多態(tài)的沖突和解決方法
例子:
現(xiàn)在有這樣一個(gè)泛型類:
class Pair<T> {
private T value;
public T getValue() {
return value;
}
public void setValue(T value) {
this.value = value;
}
}
然后我們想要一個(gè)子類繼承它溃斋。
class DateInter extends Pair<Date> {
@Override
public void setValue(Date value) {
super.setValue(value);
}
@Override
public Date getValue() {
return super.getValue();
}
}
在這個(gè)子類中界拦,我們?cè)O(shè)定父類的泛型類型為Pair<Date>,在子類中梗劫,我們覆蓋了父類的兩個(gè)方法享甸,我們的原意是這樣的:將父類的泛型類型限定為Date,那么父類里面的兩個(gè)方法的參數(shù)都為Date類型梳侨。
public Date getValue() {
return value;
}
public void setValue(Date value) {
this.value = value;
}
所以蛉威,我們?cè)谧宇愔兄貙戇@兩個(gè)方法一點(diǎn)問題也沒有,實(shí)際上走哺,從他們的@Override標(biāo)簽中也可以看到蚯嫌,一點(diǎn)問題也沒有,實(shí)際上是這樣的嗎丙躏?
分析:實(shí)際上择示,類型擦除后,父類的的泛型類型全部變?yōu)榱嗽碱愋蚈bject晒旅,所以父類編譯之后會(huì)變成下面的樣子:
class Pair {
private Object value;
public Object getValue() {
return value;
}
public void setValue(Object value) {
this.value = value;
}
}
再看子類的兩個(gè)重寫的方法的類型:
@Override
public void setValue(Date value) {
super.setValue(value);
}
@Override
public Date getValue() {
return super.getValue();
}
先來分析setValue方法栅盲,父類的類型是Object,而子類的類型是Date废恋,參數(shù)類型不一樣谈秫,這如果實(shí)在普通的繼承關(guān)系中扒寄,根本就不會(huì)是重寫,而是重載拟烫。
為什么會(huì)這樣呢旗们?
原因是這樣的,我們傳入父類的泛型類型是Date构灸,Pair<Date>上渴,我們的本意是將泛型類變?yōu)槿缦拢?/p>
class Pair {
private Date value;
public Date getValue() {
return value;
}
public void setValue(Date value) {
this.value = value;
}
}
然后再子類中重寫參數(shù)類型為Date的那兩個(gè)方法,實(shí)現(xiàn)繼承中的多態(tài)喜颁。
可是由于種種原因稠氮,虛擬機(jī)并不能將泛型類型變?yōu)镈ate,只能將類型擦除掉半开,變?yōu)樵碱愋蚈bject隔披。這樣,我們的本意是進(jìn)行重寫寂拆,實(shí)現(xiàn)多態(tài)奢米。可是類型擦除后纠永,只能變?yōu)榱酥剌d鬓长。這樣,類型擦除就和多態(tài)有了沖突尝江。JVM知道你的本意嗎涉波?知道!L啃颉啤覆!可是它能直接實(shí)現(xiàn)嗎,不能2涯簟4吧!如果真的不能的話辜纲,那我們?cè)趺慈ブ貙懳覀兿胍腄ate類型參數(shù)的方法啊笨觅。
于是JVM采用了一個(gè)特殊的方法,來完成這項(xiàng)功能侨歉,那就是橋方法屋摇。
首先,我們用javap -c className的方式反編譯下DateInter子類的字節(jié)碼幽邓,結(jié)果如下:
class com.tao.test.DateInter extends com.tao.test.Pair<java.util.Date> {
com.tao.test.DateInter();
Code:
0: aload_0
1: invokespecial #8 // Method com/tao/test/Pair."<init>":()V
4: return
public void setValue(java.util.Date); //我們重寫的setValue方法
Code:
0: aload_0
1: aload_1
2: invokespecial #16 // Method com/tao/test/Pair.setValue:(Ljava/lang/Object;)V
5: return
public java.util.Date getValue(); //我們重寫的getValue方法
Code:
0: aload_0
1: invokespecial #23 // Method com/tao/test/Pair.getValue:()Ljava/lang/Object;
4: checkcast #26 // class java/util/Date
7: areturn
public java.lang.Object getValue(); //編譯時(shí)由編譯器生成的巧方法
Code:
0: aload_0
1: invokevirtual #28 // Method getValue:()Ljava/util/Date 去調(diào)用我們重寫的getValue方法;
4: areturn
public void setValue(java.lang.Object); //編譯時(shí)由編譯器生成的巧方法
Code:
0: aload_0
1: aload_1
2: checkcast #26 // class java/util/Date
5: invokevirtual #30 // Method setValue:(Ljava/util/Date; 去調(diào)用我們重寫的setValue方法)V
8: return
}
從編譯的結(jié)果來看,我們本意重寫setValue和getValue方法的子類火脉,竟然有4個(gè)方法牵舵,其實(shí)不用驚奇柒啤,最后的兩個(gè)方法,就是編譯器自己生成的橋方法畸颅〉9可以看到橋方法的參數(shù)類型都是Object,也就是說没炒,子類中真正覆蓋父類兩個(gè)方法的就是這兩個(gè)我們看不到的橋方法涛癌。而打在我們自己定義的setvalue和getValue方法上面的@Oveerride只不過是假象。而橋方法的內(nèi)部實(shí)現(xiàn)送火,就只是去調(diào)用我們自己重寫的那兩個(gè)方法拳话。
所以,虛擬機(jī)巧妙的使用了橋方法种吸,來解決了類型擦除和多態(tài)的沖突弃衍。
不過,要提到一點(diǎn)坚俗,這里面的setValue和getValue這兩個(gè)橋方法的意義又有不同镜盯。
setValue方法是為了解決類型擦除與多態(tài)之間的沖突。
而getValue卻有普遍的意義猖败,怎么說呢速缆,如果這是一個(gè)普通的繼承關(guān)系:
那么父類的setValue方法如下:
public Object getValue() {
return super.getValue();
}
而子類重寫的方法是:
其實(shí)這在普通的類繼承中也是普遍存在的重寫,這就是協(xié)變恩闻。
并且激涤,還有一點(diǎn)也許會(huì)有疑問,子類中的橋方法Object getValue()和Date getValue()是同 時(shí)存在的判呕,可是如果是常規(guī)的兩個(gè)方法倦踢,他們的方法簽名是一樣的,也就是說虛擬機(jī)根本不能分別這兩個(gè)方法侠草。如果是我們自己編寫Java代碼辱挥,這樣的代碼是無法通過編譯器的檢查的,但是虛擬機(jī)卻是允許這樣做的边涕,因?yàn)樘摂M機(jī)通過參數(shù)類型和返回類型來確定一個(gè)方法晤碘,所以編譯器為了實(shí)現(xiàn)泛型的多態(tài)允許自己做這個(gè)看起來“不合法”的事情,然后交給虛擬器去區(qū)別功蜓。
泛型類型變量不能是基本數(shù)據(jù)類型
不能用類型參數(shù)替換基本類型园爷。就比如,沒有ArrayList<double>式撼,只有ArrayList<Double>童社。因?yàn)楫?dāng)類型擦除后,ArrayList的原始類型變?yōu)镺bject著隆,但是Object類型不能存儲(chǔ)double值扰楼,只能引用Double的值呀癣。
編譯時(shí)集合的instanceof
ArrayList<String> arrayList = new ArrayList<String>();
因?yàn)轭愋筒脸螅珹rrayList<String>只剩下原始類型弦赖,泛型信息String不存在了项栏。
那么,編譯時(shí)進(jìn)行類型查詢的時(shí)候使用下面的方法是錯(cuò)誤的
if( arrayList instanceof ArrayList<String>)
泛型在靜態(tài)方法和靜態(tài)類中的問題
泛型類中的靜態(tài)方法和靜態(tài)變量不可以使用泛型類所聲明的泛型類型參數(shù)
public class Test2<T> {
public static T one; //編譯錯(cuò)誤
public static T show(T one){ //編譯錯(cuò)誤
return null;
}
}
因?yàn)榉盒皖愔械姆盒蛥?shù)的實(shí)例化是在定義對(duì)象的時(shí)候指定的蹬竖,而靜態(tài)變量和靜態(tài)方法不需要使用對(duì)象來調(diào)用沼沈。對(duì)象都沒有創(chuàng)建,如何確定這個(gè)泛型參數(shù)是何種類型币厕,所以當(dāng)然是錯(cuò)誤的列另。
但是要注意區(qū)分下面的一種情況:
public class Test2<T> {
public static <T>T show(T one){ //這是正確的
return null;
}
}
因?yàn)檫@是一個(gè)泛型方法,在泛型方法中使用的T是自己在方法中定義的 T劈榨,而不是泛型類中的T访递。