★可以把java的泛型理解為編譯期的安全保障動(dòng)作,在編譯期保證了類型的檢查,保證了輸入輸出時(shí)的類型正確。
★比方說,一個(gè)面具舞會(huì),這個(gè)面具舞會(huì)的宗旨就是戴上面具,不問出身,不問來歷,盡情的玩,但是那是進(jìn)去參加之后的事啊,你要參加這個(gè)舞會(huì),得憑邀請(qǐng)函吧,比如說這個(gè)邀請(qǐng)函只有大咖才能擁有,人憑著邀請(qǐng)函參加舞會(huì),保安就會(huì)檢查你的身份,一看你確實(shí)是符合的,身份證押著,進(jìn)去玩吧。如果你沒有邀請(qǐng)函,說明你不能進(jìn)去,這個(gè)舞會(huì)的宗旨跟你毛關(guān)系沒有了。進(jìn)去參加的人大家都戴上了面具,就不知道你是哪里來的啊,你家住哪啊,這在泛型里叫類型擦除,你在舞會(huì)里就只是個(gè)人,玩完了,出去了,你得拿回你的身份證,好了,恢復(fù)身份,回家吧。
★在有泛型之前,我們可以用如下代碼描述舞會(huì):
import java.util.ArrayList;
import java.util.Random;
public class GenericMain {
public static void main(String[] args) {
MaskedBall maskedBall = new MaskedBall();
//
maskedBall.join(new ScienceBigShot());
maskedBall.join(new GovernmtBigShot());
//返回一個(gè)舞會(huì)的人要顯式轉(zhuǎn)型
((MaskPlayer)(maskedBall.aPlay())).play();;
}
}
//面具舞會(huì)類
class MaskedBall extends ArrayList{
private Random rd = new Random(24);
public void join(Object mp){
//檢查身份,只有是有邀請(qǐng)函的(MaskPlayer)的人才能進(jìn)去
if(MaskPlayer.class.isInstance(mp)){
this.add(mp);
((MaskPlayer)mp).play();
}
else{
throw new RuntimeException();
}
}
//隨機(jī)返回一名舞者
public Object aPlay(){
return this.get(rd.nextInt(this.size()));
}
}
//參加舞會(huì)的人
class MaskPlayer{
private String name;
public MaskPlayer(){
this.name = "無名";
}
public String getName() {
return name;
}
public void play(){
System.out.println("忘記煩惱,盡情玩");
}
}
class ScienceBigShot extends MaskPlayer{
private String source;
public ScienceBigShot(){
this.source = "科技大咖";
}
public String getSource() {
return source;
}
public void play(){
System.out.println(this.source+"(無名)忘記煩惱,盡情玩");
}
}
class GovernmtBigShot extends MaskPlayer{
private String source;
public GovernmtBigShot(){
this.source = "政府大咖";
}
public String getSource() {
return source;
}
public void play(){
System.out.println(this.source+"(無名)忘記煩惱,盡情玩");
}
}
★這樣相當(dāng)于進(jìn)舞會(huì),出舞會(huì)都需要人在那檢查身份,這多不智能啊,如果有個(gè)什么系統(tǒng)擁有判斷身份的能力就好了,泛型就給了編譯期這樣的能力。泛型使程序在邊界處進(jìn)行類型檢查和恢復(fù),在入口處(輸入)進(jìn)行類型檢查,隨后類型就被擦除了,在出口處進(jìn)行身份恢復(fù),一般都是加個(gè)顯示類型轉(zhuǎn)換,java1.5以后這些入口出口的工作都是我們在做的,1.5之后這些工作教給編譯期來做,下面是加入了泛型的舞會(huì)代碼。
import java.util.ArrayList;
import java.util.Random;
public class GenericMain {
public static void main(String[] args) {
MaskedBall maskedBall = new MaskedBall();
//
maskedBall.join(new ScienceBigShot());
maskedBall.join(new GovernmtBigShot());
//返回一個(gè)舞會(huì)的人要顯式轉(zhuǎn)型
maskedBall.aPlay().play();
}
}
//面具舞會(huì)類
class MaskedBall<T extends MaskPlayer> extends ArrayList<T>{
private Random rd = new Random(24);
public void join(T mp){
//檢查身份,只有是有邀請(qǐng)函的(MaskPlayer)的人才能進(jìn)去,現(xiàn)在編譯器自己就會(huì)幫我們進(jìn)行類型檢查
this.add(mp);
mp.play();
}
//隨機(jī)返回一名舞者
public T aPlay(){
return this.get(rd.nextInt(this.size()));
}
}
//參加舞會(huì)的人
class MaskPlayer{
private String name;
public MaskPlayer(){
this.name = "無名";
}
public String getName() {
return name;
}
public void play(){
System.out.println("忘記煩惱,盡情玩");
}
}
class ScienceBigShot extends MaskPlayer{
private String source;
public ScienceBigShot(){
this.source = "科技大咖";
}
public String getSource() {
return source;
}
public void play(){
System.out.println(this.source+"(無名)忘記煩惱,盡情玩");
}
}
class GovernmtBigShot extends MaskPlayer{
private String source;
public GovernmtBigShot(){
this.source = "政府大咖";
}
public String getSource() {
return source;
}
public void play(){
System.out.println(this.source+"(無名)忘記煩惱,盡情玩");
}
}
★這樣我們就會(huì)省去很多代碼,程序還不容易出錯(cuò)。
★泛型的類型擦除指的是java編譯之后字節(jié)碼文件中并沒有存儲(chǔ)具體的類型參數(shù),比如List<String>,List<Integer>,字節(jié)碼文件中只能看到List,String和Integer的信息都看不到,這就是被擦除了,它們都被替換為Object。
★T,?,類型擦除后被替換為Object。
★? extends x上界限定,不可輸入,可輸出,類型擦除后被替換為x。
★? super x下界限定,可輸入x以及x的子類,不可輸出,類型擦除后被替換為Object。
?數(shù)組可以協(xié)變,集合不可以協(xié)變。
比如: Integer是Number的子類,則Integer[]是Number[]的子類
,但是List<Integer>不是List<Number>的子類。
數(shù)組的協(xié)變本身就是一種設(shè)計(jì)的缺陷,比如
Number[] ns = new Integer[10];
ns[0] = new Float(0.0);//error
你總不能像一個(gè)類型是Integer數(shù)組的數(shù)組里放一個(gè)Float。
集合修復(fù)了這個(gè)缺陷。但是為了有時(shí)候的必要,設(shè)計(jì)了上下界限定符。
★沒有泛型數(shù)組
數(shù)組在創(chuàng)建的時(shí)候必須知道內(nèi)部元素的類型,而且一直都會(huì)記得這個(gè)類型信息,每次往數(shù)組里添加元素,都會(huì)做類型檢查。
但因?yàn)镴ava泛型是用擦除(Erasure)實(shí)現(xiàn)的,運(yùn)行時(shí)類型參數(shù)會(huì)被擦掉。
所以,像List<String>[] l = new ArrayList<String>[10]; 這樣的代碼,運(yùn)行時(shí)編譯期只能看到ArrayList,看不到具體的類型參數(shù)。所以不允許創(chuàng)建泛型數(shù)組。