泛型的作用
- 多種數(shù)據(jù)類型執(zhí)行相同的代碼
- 規(guī)范數(shù)據(jù)類型骂维,在編譯時發(fā)出警告,防止出現(xiàn)cast異常
泛型的使用
- 泛型類:
public class NormalGeneric<T> {
private T data;
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
}
public static void main(String[] args) {
NormalGeneric<String> normalGeneric = new NormalGeneric<>();
normalGeneric.setData("12345");
System.out.println(normalGeneric.getData()); // 輸出12345
}
聲明了一個泛型類,泛型為T冰单,T類似一個形參,在使用這個類的時候為這個T傳入一個實參灸促,類中所有用到的T都替換成實參String球凰,實參必須是一個對象類型;
- 泛型接口:
public interface People<T>{
public T talk();
}
定義了一個People接口腿宰,指定了一個泛型T呕诉,接口內(nèi)部有一個talk抽象方法;
實現(xiàn)方式1:泛型類實現(xiàn)泛型接口
class Student<T> implements People<T>{
@Override
public T talk() {
return null;
}
}
實現(xiàn)方式2:普通類實現(xiàn)泛型接口(直接指定泛型的實際類型)
class Teacher implements People<String>{
@Override
public String talk() {
return null;
}
}
- 泛型方法:
public <T> T plus(){
T result = null;
// todo
return result;
}
在使用這個方法的時候指定泛型的實際類型
normalGeneric.<Integer>plus(123);
也可以省去泛型的實際類型吃度,編譯器借助傳入的參數(shù)會自動設置泛型的值
normalGeneric.plus("abc");
泛型變量的限定
public <T extends Comparable> T max(T a,T b){
return a.compareTo(b)>0?a:b;
}
class Man<T extends Comparable>{
T man;
public T getMan() {
man.compareTo(man); // 具有Compareable接口屬性
return man;
}
}
如果我們需要泛型變量具有特定接口的屬性甩挫,可以給泛型T 繼承一個接口,用來限定這個泛型的變量必須擁有這個接口的屬性椿每,傳入的a和b必須實現(xiàn)Compareable接口伊者;
T的extends后面英遭,如果有類必須寫在第一個,并且只能有一個類亦渗,可以有多個接口(單繼承挖诸,多實現(xiàn))
泛型的局限
不能實例化泛型類型:不能new T()
靜態(tài)域不能引用類型變量:應為static靜態(tài)屬性先加載,而泛型的確認是在對象new的時候才知道法精;
必須是包裝類型多律,不能是基礎類型
-
不能使用instanceof判斷對象類型
-
泛型類不能繼承異常
泛型的通配符
類型之間的繼承關系不能保證泛型之間的繼承關系
下面舉一個例子說明這句話的意思
首先聲明了如下四個繼承關系的類
public class Food {
}
public class Fruit extends Food {
}
public class Apple extends Fruit {
}
public class Orange extends Fruit {
}
再聲明一個標準的泛型類
public class GenericType<T> {
private T data;
public void setData(T data) {
this.data = data;
}
public T getData() {
return data;
}
}
我們聲明了一個eat方法,傳入的參數(shù)是泛型為Fruit的標準泛型類搂蜓,創(chuàng)建了兩個標準泛型類狼荞,一個泛型是Fruit,一個泛型是Apple帮碰,當調(diào)用eat方法時相味,只有fruit有效,apple卻不行殉挽,可是apple明明繼承了fruit丰涉,也是水果,為什么不能吃呢斯碌?一死?
因為類型的繼承不能保證泛型變量之間的繼承關系,為了解決吃apple的問題输拇,引入了通配符 摘符?
我們將泛型的"實參"改成
? extends Fruit
,表示接收上界為Fruit的類型(Fruit和Fruit的子類)
下面再理解一句話:通配符用于安全的訪問數(shù)據(jù)
為什么setdata()的時候不管傳入fruit還是apple都是不行的策吠,因為通配符? extends Fruit
規(guī)定了可能是Fruit逛裤,可能是Apple,也可能是Orange猴抹,不確定傳入的是哪個带族,所以set的時候都不可以,但是在取出的時候蟀给,有一點可以保證蝙砌,那就是一定是Fruit,(因為Apple和Orange也是Fruit)跋理,所以通配符用于安全的訪問數(shù)據(jù)
通配符只能用在泛型方法上择克,不可以用在泛型類上
? super Apple
表示Apple 和 Apple 的父類:安全的寫入數(shù)據(jù)
super通配符
可以set當前類和其子類,get Object類
extends通配符
不能set 前普, 只能get當前類和其父類
泛型的原理
類型擦除:(在編譯期完成)
在jdk中的泛型是偽泛型肚邢,泛型類型在編譯的時候會被擦除,jvm看到的只有原始類型ArrayList<String> 和 ArrayList<Object> 在jvm看來是一樣的