泛型,就是參數(shù)化類型的意思,具體表現(xiàn)為泛型類,泛型接口,泛型方法。
泛型主要用于編譯過程不確定參數(shù)可能的類型,需要對參數(shù)進(jìn)行統(tǒng)一邏輯處理,比較常用在設(shè)計(jì)模式里。
泛型只在編譯階段有作用,編譯器檢查泛型結(jié)果無誤后會擦去泛型相關(guān)信息,并且在對象進(jìn)入和離開的邊界加入類型檢查和類型轉(zhuǎn)換的方法。泛型不會進(jìn)入到運(yùn)行時(shí)階段。
public class TestFanxing {
public static void main(String[] args) {
test1();
}
public static void test1() {
// 泛型只在編譯時(shí)有作用,編譯器檢查完泛型類型后,會擦去泛型信息,只在對象進(jìn)入和離開的邊界出加入類型檢查和轉(zhuǎn)換的方法。
// 運(yùn)行時(shí)不存在泛型概念。
List<String> stringArrayList = new ArrayList<String>();
List<Integer> integerArrayList = new ArrayList<Integer>();
Class classStringArrayList = stringArrayList.getClass();
Class classIntegerArrayList = integerArrayList.getClass();
if(classStringArrayList.equals(classIntegerArrayList)){
System.out.println("泛型測試,泛型相同");
}
}
}
結(jié)果:
泛型測試,泛型相同
泛型類
定義類的時(shí)候使用了泛型,該類就是泛型類。常見使用的有List,Set,Map。
一個(gè)基本的泛型類:
public class Generic<T>{
//key這個(gè)成員變量的類型為T,T的類型由外部指定,代表泛型的字母可以隨便寫
private T key;
public Generic(T key) { //泛型構(gòu)造方法形參key的類型也為T,T的類型由外部指定
this.key = key;
}
public T getKey(){ //泛型方法getKey的返回值類型為T,T的類型由外部指定
return key;
}
}
泛型的類型參數(shù)只能是類類型,不能是簡單類型。當(dāng)然也可以不傳,默認(rèn)可以是任意類型。
public static void test2() {
class Generic<T> {
private T key;
public Generic(T key) {
this.key = key;
}
public T getKey() {
return key;
}
}
//泛型的類型參數(shù)只能是類類型(包括自定義類),不能是簡單類型
//傳入的實(shí)參類型需與泛型的類型參數(shù)類型相同,即為Integer.
Generic<Integer> genericInteger = new Generic<Integer>(123456);
//傳入的實(shí)參類型需與泛型的類型參數(shù)類型相同,即為String.
Generic<String> genericString = new Generic<String>("key_vlaue");
System.out.println("泛型測試" + "key is " + genericInteger.getKey());
System.out.println("泛型測試" + "key is " + genericString.getKey());
// 如果定義時(shí)不傳入泛型實(shí)參,則默認(rèn)可以是任意類型
Generic generic = new Generic("111111");
Generic generic1 = new Generic(4444);
Generic generic2 = new Generic(55.55);
Generic generic3 = new Generic(false);
System.out.println("泛型測試" + "key is " + generic.getKey());
System.out.println("泛型測試" + "key is " + generic1.getKey());
System.out.println("泛型測試" + "key is " + generic2.getKey());
System.out.println("泛型測試" + "key is " + generic3.getKey());
}
結(jié)果:
泛型測試key is 123456
泛型測試key is key_vlaue
泛型測試key is 111111
泛型測試key is 4444
泛型測試key is 55.55
泛型測試key is false
不能對確切的泛型類型使用instanceof操作。編譯時(shí)會出錯(cuò)。
泛型接口
泛型接口的使用與泛型類級別相同
//定義一個(gè)泛型接口
public interface Generator<T> {
public T next();
}
實(shí)現(xiàn)泛型接口的時(shí)候,定義要加上泛型聲明,否則會報(bào)錯(cuò)。
/**
* 未傳入泛型實(shí)參時(shí),與泛型類的定義相同,在聲明類的時(shí)候,需將泛型的聲明也一起加到類中。
* 即:class FruitGenerator<T> implements Generator<T>{
* 如果不聲明泛型,如:class FruitGenerator implements Generator<T>,編譯器會報(bào)錯(cuò):"Unknown class"
*/
class FruitGenerator<T> implements Generator<T>{
@Override
public T next() {
return null;
}
}
/**
* 傳入泛型實(shí)參時(shí):
* 定義一個(gè)生產(chǎn)器實(shí)現(xiàn)這個(gè)接口,雖然我們只創(chuàng)建了一個(gè)泛型接口Generator<T>
* 但是我們可以為T傳入無數(shù)個(gè)實(shí)參,形成無數(shù)種類型的Generator接口。
* 在實(shí)現(xiàn)類實(shí)現(xiàn)泛型接口時(shí),如已將泛型類型傳入實(shí)參類型,則所有使用泛型的地方都要替換成傳入的實(shí)參類型
* 即:Generator<T>,public T next();中的的T都要替換成傳入的String類型。
*/
public class FruitGenerator implements Generator<String> {
private String[] fruits = new String[]{"Apple", "Banana", "Pear"};
@Override
public String next() {
Random rand = new Random();
return fruits[rand.nextInt(3)];
}
}
泛型通配符
泛型通配符簡單看就是一個(gè)“?”,但是千萬不要跟泛型形參搞混,它是泛型實(shí)參!相當(dāng)于我們傳入了實(shí)際的Number,Integer等,可以把“?”看成所有類型的父類,是一種真實(shí)的類型。
這個(gè)通配符解決的問題也很明顯,就是使用泛型實(shí)參的時(shí)候,如果不確定可能的類型,就用這個(gè)。
泛型方法
泛型方法的聲明基本是下面這個(gè)樣子:
public <E> void show_3(E t){ // <E>是必須的,否則就是普通的方法
System.out.println(t.toString());
}
普通類中的泛型方法
使用要注意,<>中的符號不是隨便寫的,應(yīng)該是可之前定義過的泛型形參,仔細(xì)看下面的例子來幫助理解。
public class GenericTest {
public class Generic<T>{
private T key;
public Generic(T key) {
this.key = key;
}
//我想說的其實(shí)是這個(gè),雖然在方法中使用了泛型,但是這并不是一個(gè)泛型方法。
//這只是類中一個(gè)普通的成員方法,只不過他的返回值是在聲明泛型類已經(jīng)聲明過的泛型。
//所以在這個(gè)方法中才可以繼續(xù)使用 T 這個(gè)泛型。
public T getKey(){
return key;
}
/**
* 這個(gè)方法顯然是有問題的,在編譯器會給我們提示這樣的錯(cuò)誤信息"cannot reslove symbol E"
* 因?yàn)樵陬惖穆暶髦胁⑽绰暶鞣盒虴,所以在使用E做形參和返回值類型時(shí),編譯器會無法識別。
public E setKey(E key){
this.key = keu
}
*/
}
//這也不是一個(gè)泛型方法,這就是一個(gè)普通的方法,只是使用了Generic<Number>這個(gè)泛型類做形參而已。
public void showKeyValue1(Generic<Number> obj){
Log.d("泛型測試","key value is " + obj.getKey());
}
//這也不是一個(gè)泛型方法,這也是一個(gè)普通的方法,只不過使用了泛型通配符?
//同時(shí)這也印證了泛型通配符章節(jié)所描述的,?是一種類型實(shí)參,可以看做為Number等所有類的父類
public void showKeyValue2(Generic<?> obj){
Log.d("泛型測試","key value is " + obj.getKey());
}
/**
* 這個(gè)方法是有問題的,編譯器會為我們提示錯(cuò)誤信息:"UnKnown class 'E' "
* 雖然我們聲明了<T>,也表明了這是一個(gè)可以處理泛型的類型的泛型方法。
* 但是只聲明了泛型類型T,并未聲明泛型類型E,因此編譯器并不知道該如何處理E這個(gè)類型。
public <T> T showKeyName(Generic<E> container){
...
}
*/
/**
* 這個(gè)方法也是有問題的,編譯器會為我們提示錯(cuò)誤信息:"UnKnown class 'T' "
* 對于編譯器來說T這個(gè)類型并未項(xiàng)目中聲明過,因此編譯也不知道該如何編譯這個(gè)類。
* 所以這也不是一個(gè)正確的泛型方法聲明。
public void showkey(T genericObj){
}
*/
/**
正確的普通類中的泛型方法
*/
public <T> T showKeyName(Generic<T> container){
System.out.println("container key :" + container.getKey());
//當(dāng)然這個(gè)例子舉的不太合適,只是為了說明泛型方法的特性。
T test = container.getKey();
return test;
}
}
泛型類中的泛型方法
public void show_1(T t){
System.out.println(t.toString());
}
//在泛型類中聲明了一個(gè)泛型方法,使用泛型E,這種泛型E可以為任意類型??梢灶愋团cT相同,也可以不同。
//由于泛型方法在聲明的時(shí)候會聲明泛型<E>,因此即使在泛型類中并未聲明泛型,編譯器也能夠正確識別泛型方法中識別的泛型。
public <E> void show_3(E t){
System.out.println(t.toString());
}
//在泛型類中聲明了一個(gè)泛型方法,使用泛型T,注意這個(gè)T是一種全新的類型,可以與泛型類中聲明的T不是同一種類型。
public <T> void show_2(T t){
System.out.println(t.toString());
}
}
簡單來說
沒在“<>”中聲明過就不要直接用,編譯器不認(rèn)識。
靜態(tài)方法與泛型
靜態(tài)方法無法訪問到類定義的泛型。
如果靜態(tài)方法要使用泛型,就要額外用<>聲明
public class StaticGenerator<T> {
/**
* 如果在類中定義使用泛型的靜態(tài)方法,需要添加額外的泛型聲明(將這個(gè)方法定義成泛型方法)
* 即使靜態(tài)方法要使用泛型類中已經(jīng)聲明過的泛型也不可以。
* 如:public static void show(T t){..},此時(shí)編譯器會提示錯(cuò)誤信息:
"StaticGenerator cannot be refrenced from static context"
*/
public static <T> void show(T t){
}
}
泛型邊界
指的是泛型也可以有繼承關(guān)系,用來限制形參的范圍。
泛型通配符設(shè)置邊界
public void showKeyValue1(Generic<? extends Number> obj){
Log.d("泛型測試","key value is " + obj.getKey());
}
Generic<String> generic1 = new Generic<String>("11111");
//這一行代碼編譯器會提示錯(cuò)誤,因?yàn)镾tring類型并不是Number類型的子類showKeyValue1(generic1);
泛型類定義的時(shí)候也可以設(shè)置邊界
public class Generic<T extends Number>{
private T key;
public Generic(T key) {
this.key = key;
}
public T getKey(){
return key;
}
}
泛型方法設(shè)置邊界
//在泛型方法中添加上下邊界限制的時(shí)候,必須在權(quán)限聲明與返回值之間的<T>上添加上下邊界,即在泛型聲明的時(shí)候添加
//public <T> T showKeyName(Generic<T extends Number> container),編譯器會報(bào)錯(cuò):"Unexpected bound"
public <T extends Number> T showKeyName(Generic<T> container){
System.out.println("container key :" + container.getKey());
T test = container.getKey();
return test;
}
關(guān)于泛型數(shù)組
在java中是不能創(chuàng)建一個(gè)確切的泛型類型的數(shù)組的。只能用默認(rèn)類型或者泛型通配符。
List<String>[] ls = new ArrayList<String>[10]; // 不被允許
List<?>[] ls = new ArrayList<?>[10]; // 允許
List<String>[] ls = new ArrayList[10]; // 允許
// List<String> list0 = new ArrayList<String>[3]; // java不允許創(chuàng)建明確泛型類型的數(shù)組
List<?>[] list = new ArrayList[3]; // 創(chuàng)建了一個(gè)List類型的數(shù)組,List是個(gè)泛型類,即創(chuàng)建了一個(gè)泛型的數(shù)組
ArrayList<Integer> value = new ArrayList<Integer>(); // 準(zhǔn)備一個(gè)數(shù)組元素
value.add(5);
list[0] = value; // 賦值
Integer r = (Integer) list[0].get(0); // 如果定義數(shù)組沒有使用通配符?而是明確的Integer,這里就不需要強(qiáng)轉(zhuǎn)
參考文章:
java 泛型詳解