泛型的本質是為了參數(shù)化類型研乒。也就是說在泛型使用過程中乏盐,操作的數(shù)據(jù)類型被指定為一個參數(shù)佳窑,這種參數(shù)類型可以用在類、接口和方法中父能,分別被稱為泛型類神凑、泛型接口、泛型方法
- 泛型只在編譯過程中有效, 在運行階段失效;
- 泛型的類型參數(shù)只能是類類型法竞,不能是簡單類型
定義格式
class 類名稱 <泛型標識:可以隨便寫任意標識號耙厚,標識指定的泛型的類型{
private 泛型標識 /*(成員變量類型)*/ var;
.....
}
}
泛型類
//此處T可以隨便寫為任意標識强挫,常見的如T、E薛躬、K俯渤、V等形式的參數(shù)常用于表示泛型
//在實例化泛型類時,必須指定T的具體類型(注意: 這里T代表具體類型,非簡單類型)
public class Generic<T>{
//key這個成員變量的類型為T,T的類型由外部指定
private T key;
public Generic(T key) { //泛型構造方法形參key的類型也為T型宝,T的類型由外部指定
this.key = key;
}
public T getKey(){ //泛型方法getKey的返回值類型為T八匠,T的類型由外部指定
return key;
}
}
//泛型的類型參數(shù)只能是類類型(包括自定義類),不能是簡單類型
//傳入的實參類型需與泛型的類型參數(shù)類型相同趴酣,即為Integer.
Generic<Integer> genericInteger = new Generic<Integer>(123456);
//傳入的實參類型需與泛型的類型參數(shù)類型相同梨树,即為String.
Generic<String> genericString = new Generic<String>("key_vlaue");
Log.d("泛型測試","key is " + genericInteger.getKey());
Log.d("泛型測試","key is " + genericString.getKey());
12-27 09:20:04.432 13063-13063/? D/泛型測試: key is 123456
12-27 09:20:04.432 13063-13063/? D/泛型測試: key is key_vlaue
在實例化時不使用泛型實參
的話, 則可以定義為任何類型
Generic generic = new Generic("111111");
Generic generic1 = new Generic(4444);
Generic generic2 = new Generic(55.55);
Generic generic3 = new Generic(false);
Log.d("泛型測試","key is " + generic.getKey());
Log.d("泛型測試","key is " + generic1.getKey());
Log.d("泛型測試","key is " + generic2.getKey());
Log.d("泛型測試","key is " + generic3.getKey());
輸出結果:
D/泛型測試: key is 111111
D/泛型測試: key is 4444
D/泛型測試: key is 55.55
D/泛型測試: key is false
泛型接口
- 泛型接口常被用在各種類的生產器中;
- 定義:
//定義一個泛型接口
public interface Generator<T> {
public T next();
}
使用:
/**
* 傳入泛型實參時:
* 定義一個生產器實現(xiàn)這個接口,雖然我們只創(chuàng)建了一個泛型接口Generator<T>
* 但是我們可以為T傳入無數(shù)個實參,形成無數(shù)種類型的Generator接口岖寞。
* 在實現(xiàn)類實現(xiàn)泛型接口時抡四,如已將泛型類型傳入實參類型,則所有使用泛型的地方都要替換成傳入的實參類型
* 即: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)];
}
}
通配符
Generic<Integer> gen1 = new Generic<Integer>(12345);
Generic<String> gen2 = Generic<String>("Hello liangxifeng");
showKeyValue1(gen2);
public void showKeyValue1(Generic<Integer> obj){
System.out.println("泛型測試key value is " + obj.getKey());
}
以上運行會報錯:
// showKeyValue這個方法編譯器會為我們報錯:Generic<java.lang.String>
// cannot be applied to Generic<java.lang.Integer>
// showKeyValue(gInteger);
使用統(tǒng)配符 ?
解決
public void showKeyValue1(Generic<?> obj){
System.out.println("泛型測試key value is " + obj.getKey());
}
以上程序在運行就不會報錯了
此處’?’是類型實參隶垮,而不是類型形參 藻雪!再直白點的意思就是,此處的狸吞?和Number勉耀、String、Integer一樣都是一種實際的類型蹋偏,可以把便斥?看成所有類型的父類。是一種真實的類型
泛型方法
- 泛型類暖侨,是在實例化類的時候指明泛型的具體類型椭住;
- 泛型方法崇渗,是在調用方法的時候指明泛型的具體類型 字逗。
- 必須使用 public <T>方式聲明才叫泛型方法, 新聲明的泛型方法中的T與所屬類中聲明的泛型T Class<T>可以不一致;
/**
* 泛型方法的基本介紹
* @param tClass 傳入的泛型實參
* @return T 返回值為T類型
* 說明:
* 1)public 與 返回值中間<T>非常重要,可以理解為聲明此方法為泛型方法宅广。
* 2)只有聲明了<T>的方法才是泛型方法葫掉,泛型類中的使用了泛型的成員方法并不是泛型方法。
* 3)<T>表明該方法將使用泛型類型T跟狱,此時才可以在方法中使用泛型類型T俭厚。
* 4)與泛型類的定義一樣,此處T可以隨便寫為任意標識驶臊,常見的如T挪挤、E叼丑、K、V等形式的參數(shù)常用于表示泛型扛门。
*/
public <T> T genericMethod(Class<T> tClass)throws InstantiationException ,
IllegalAccessException{
T instance = tClass.newInstance();
return instance;
}
詳細說明
public class GenericTest {
//這個類是個泛型類鸠信,在上面已經介紹過
public class Generic<T>{
private T key;
public Generic(T key) {
this.key = key;
}
//我想說的其實是這個,雖然在方法中使用了泛型论寨,但是這并不是一個泛型方法星立。
//這只是類中一個普通的成員方法,只不過他的返回值是在聲明泛型類已經聲明過的泛型葬凳。
//所以在這個方法中才可以繼續(xù)使用 T 這個泛型绰垂。
public T getKey(){
return key;
}
/**
* 這個方法顯然是有問題的,在編譯器會給我們提示這樣的錯誤信息"cannot reslove symbol E"
* 因為在類的聲明中并未聲明泛型E火焰,所以在使用E做形參和返回值類型時劲装,編譯器會無法識別。
public E setKey(E key){
this.key = keu
}
*/
}
/**
* 這才是一個真正的泛型方法昌简。
* 首先在public與返回值之間的<T>必不可少酱畅,這表明這是一個泛型方法,并且聲明了一個泛型T
* 這個T可以出現(xiàn)在這個泛型方法的任意位置.
* 泛型的數(shù)量也可以為任意多個
* 如:public <T,K> K showKeyName(Generic<T> container){
* ...
* }
*/
public <T> T showKeyName(Generic<T> container){
System.out.println("container key :" + container.getKey());
//當然這個例子舉的不太合適江场,只是為了說明泛型方法的特性纺酸。
T test = container.getKey();
return test;
}
//這也不是一個泛型方法,這就是一個普通的方法址否,只是使用了Generic<Number>這個泛型類做形參而已餐蔬。
public void showKeyValue1(Generic<Number> obj){
Log.d("泛型測試","key value is " + obj.getKey());
}
//這也不是一個泛型方法,這也是一個普通的方法佑附,只不過使用了泛型通配符?
//同時這也印證了泛型通配符章節(jié)所描述的樊诺,?是一種類型實參,可以看做為Number等所有類的父類
public void showKeyValue2(Generic<?> obj){
Log.d("泛型測試","key value is " + obj.getKey());
}
/**
* 這個方法是有問題的音同,編譯器會為我們提示錯誤信息:"UnKnown class 'E' "
* 雖然我們聲明了<T>,也表明了這是一個可以處理泛型的類型的泛型方法词爬。
* 但是只聲明了泛型類型T,并未聲明泛型類型E权均,因此編譯器并不知道該如何處理E這個類型顿膨。
public <T> T showKeyName(Generic<E> container){
...
}
*/
/**
* 這個方法也是有問題的,編譯器會為我們提示錯誤信息:"UnKnown class 'T' "
* 對于編譯器來說T這個類型并未項目中聲明過叽赊,因此編譯也不知道該如何編譯這個類恋沃。
* 所以這也不是一個正確的泛型方法聲明。
public void showkey(T genericObj){
}
*/
public static void main(String[] args) {
}
}
public class GenericFruit {
class Fruit{
@Override
public String toString() {
return "fruit";
}
}
class Apple extends Fruit{
@Override
public String toString() {
return "apple";
}
}
class Person{
@Override
public String toString() {
return "Person";
}
}
class GenerateTest<T>{
public void show_1(T t){
System.out.println(t.toString());
}
//在泛型類中聲明了一個泛型方法必指,使用泛型E囊咏,這種泛型E可以為任意類型。可以類型與T相同梅割,也可以不同霜第。
//由于泛型方法在聲明的時候會聲明泛型<E>,因此即使在泛型類中并未聲明泛型户辞,編譯器也能夠正確識別泛型方法中識別的泛型庶诡。
public <E> void show_3(E t){
System.out.println(t.toString());
}
//在泛型類中聲明了一個泛型方法,使用泛型T咆课,注意這個T是一種全新的類型末誓,可以與泛型類中聲明的T不是同一種類型。
public <T> void show_2(T t){
System.out.println(t.toString());
}
}
public static void main(String[] args) {
Apple apple = new Apple();
Person person = new Person();
GenerateTest<Fruit> generateTest = new GenerateTest<Fruit>();
//apple是Fruit的子類书蚪,所以這里可以
generateTest.show_1(apple);
//編譯器會報錯喇澡,因為泛型類型實參指定的是Fruit,而傳入的實參類是Person
//generateTest.show_1(person);
//使用這兩個方法都可以成功
generateTest.show_2(apple);
generateTest.show_2(person);
//使用這兩個方法也都可以成功
generateTest.show_3(apple);
generateTest.show_3(person);
}
}
泛型方法與可變參數(shù)
public <T> void printMsg( T... args){
for(T t : args){
Log.d("泛型測試","t is " + t);
}
}
輸出:
printMsg("111",222,"aaaa","2323.4",55.55);
靜態(tài)方法與泛型
- 靜態(tài)方法無法訪問類上定義的泛型殊校;
- 如果靜態(tài)方法操作的引用數(shù)據(jù)類型不確定的時候晴玖,必須要將泛型定義在方法上
public class StaticGenerator<T> {
....
....
/**
* 如果在類中定義使用泛型的靜態(tài)方法,需要添加額外的泛型聲明(將這個方法定義成泛型方法)
* 即使靜態(tài)方法要使用泛型類中已經聲明過的泛型也不可以为流。
* 如:public static void show(T t){..},此時編譯器會提示錯誤信息:
"StaticGenerator cannot be refrenced from static context"
*/
public static <T> void show(T t){
}
}
泛型方法的總結
- 無論何時呕屎,如果你能做到,你就該盡量使用泛型方法敬察。
- 也就是說秀睛,如果使用泛型方法將整個類泛型化,那么就應該使用泛型方法莲祸。
- 另外對于一個static的方法而已蹂安,無法訪問泛型類型的參數(shù)。所以如果static方法要使用泛型能力锐帜,就必須使其成為泛型方法田盈。
泛型上下邊界
- 在使用泛型的時候,我們還可以為傳入的泛型類型實參進行上下邊界的限制缴阎,如:類型實參只準傳入某種類型的父類或某種類型的子類允瞧。
- 為泛型添加上邊界,即傳入的類型實參必須是指定類型的子類型
public void showKeyValue1(Generic<? extends Number> obj){
Log.d("泛型測試","key value is " + obj.getKey());
}
Generic<String> generic1 = new Generic<String>("11111");
Generic<Integer> generic2 = new Generic<Integer>(2222);
Generic<Float> generic3 = new Generic<Float>(2.4f);
Generic<Double> generic4 = new Generic<Double>(2.56);
//這一行代碼編譯器會提示錯誤蛮拔,因為String類型并不是Number類型的子類
//showKeyValue1(generic1);
showKeyValue1(generic2);
showKeyValue1(generic3);
showKeyValue1(generic4);
我們修改一下泛型的定義
public class Generic<T extends Number>{
private T key;
public Generic(T key) {
this.key = key;
}
public T getKey(){
return key;
}
}
//這一行代碼也會報錯述暂,因為String不是Number的子類
Generic<String> generic1 = new Generic<String>("11111");
- 泛型的上下邊界添加,必須與泛型的聲明在一起
//在泛型方法中添加上下邊界限制的時候语泽,必須在權限聲明與返回值之間的<T>上添加上下邊界贸典,即在泛型聲明的時候添加
//public <T> T showKeyName(Generic<T extends Number> container),編譯器會報錯:"Unexpected bound"
public <T extends Number> T showKeyName(Generic<T> container){
System.out.println("container key :" + container.getKey());
T test = container.getKey();
return test;
}
泛型數(shù)組
- 不能創(chuàng)建一個確切的泛型類型的數(shù)組, 但是使用通配符
?
使可以的
//錯誤寫法
List<String>[] ls = new ArrayList<String>[10];
//正確寫法
List<?>[] lsa = new ArrayList<?>[10];
//正確寫法
List<String>[] lsb = new ArrayList[10];
參考文章: http://m.blog.csdn.net/s10461/article/details/53941091
泛型方法定義和引用參考: http://www.cnblogs.com/iyangyuan/archive/2013/04/09/3011274.html
聊一聊-JAVA 泛型中的通配符 T踱卵,E,K,V惋砂,妒挎?:
https://juejin.im/post/5d5789d26fb9a06ad0056bd9?utm_source=gold_browser_extension