java泛型:
- 泛型簡介
- 泛型類
- 限制泛型可用類型
- 類型通配聲明
- 泛型方法
問題: 如果我們需要產生多個對象,每個對象的邏輯完全一樣,只是對象內的成員變量的類型不同。那我們該如何去做?如果按照正常的做法就要創建多個類文件,給每個類中的成員變量設置指定的數據類型。這樣做的缺點: 這樣會導致類膨脹,重用太差。解決方法: 創建一個類文件,給這個類中2成員變量設置Object(是所有類的父類)數據類型。 缺點: 編譯的時候正常,但運行的時候可能會出現異常。因為Object導致整個類對變量的屬性很模糊。面對這樣的問題在JDK1.5以后引入泛型 。
class Cla1
{Object a;public Cla1(Object a) {this.a=a;}public Object getData(){return a;}
}
//class cla2
//{
// String a;
// public cla2(String a) {
// this.a=a;
// }
// public String getData(){
// return a;
// }
//}
public class Mian {public static void main(String[] args) {Cla1 c1=new Cla1("String");System.out.println(c1.getData());//System.out.println((Integer)c1.getData());Object導致整個類對變量的屬性很模糊,在強制類型轉換的時候會出現下列錯誤//Exception in thread "main" java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Integer//at Mian.main(Mian.java:25)}
}
泛型簡介:
- 泛型可以在編譯的時候檢查類型安全,并且所有的強制轉換都是自動的和隱式的。
- 泛型的原理就是 類型的參數化,即把類型看做參數。也就是說把所有要操作的數據類型看做參數,就像方法的形式參數是運行時傳遞的值的占位符一樣。
- 簡單的說,類型變量 扮演的角色就如同一個參數,它是提供給編譯器用來類型檢查的信息。
- 泛型可以提高代碼的擴展性和重用性。
class Cla1<T>//T必須是引用類型
{T a;public Cla1(T a) {this.a=a;}public T getData(){return a;}
}
public class Mian {public static void main(String[] args) {Cla1<Integer> c1=new Cla1<Integer>(10);//<Integer>明確類中參數的類型,相當于類型的參數化,用<>表示//Cla1<int> c1=new Cla1<int>(10);這樣寫會報錯,<>括號內的必須是引用類型,System.out.println(c1.getData());}
}
泛型類及特點:
- 泛型的類型參數可以是泛型類
- 泛型類可以同時設置多個類型參數
- 泛型類可以繼承泛型類
- 泛型類可以實現泛型接口
泛型類可以同時設置多個類型參數
class Cla1<T> //一個類型參數
{T a;public Cla1(T a) {this.a=a;}public T getData(){return a;}
}
class Cla2<T1,T2> //多個類型參數
{T1 a;T2 b;public Cla2(T1 a,T2 b) {this.a=a;this.b=b;}public T1 getDataA(){return a;}public T2 getDataB(){return b;}
}
public class Mian {public static void main(String[] args) {Cla1<Integer> c1=new Cla1<Integer>(10);System.out.println(c1.getData());Cla2<Integer,String> c2=new Cla2<Integer,String>(30,"輸出");System.out.println(c2.getDataA());System.out.println(c2.getDataB());System.out.println(c2.getDataA()+c2.getDataB());//數字和字符串相加,+起連接作用Cla2<Integer,Integer> c3=new Cla2<Integer,Integer>(100,120);System.out.println(c3.getDataA());System.out.println(c3.getDataB());System.out.println(c3.getDataA()+c3.getDataB());//數字和數字相加,+就是數值相加 //有的博客說這里的數值不能直接相加 }
}
泛型類型可以是泛型類:
class Cla1<T> //一個類型參數
{T a;public Cla1(T a) {this.a=a;}public T getData(){return a;}
}
public class Main {public static void main(String[] args) {Cla1<Cla1<Integer>> c1=new Cla1<Cla1<Integer>>(new Cla1<Integer>(10));//泛型類里面的類型還是泛型類,傳參的時候傳的是泛型類的引用System.out.println(c1.getData().getData());//c1.getData()是調用的當做參數傳入的泛型類,Cla1<Integer> a,就相當于這樣的//此時a是泛型類類型,需要再對a取內容才能得到真正的a的數值,也就是當做參數傳進去的那個泛型類的構造方法的數值}
}
泛型類可以繼承泛型類:
abstract class Cla1<T> //一個類型參數
{T a;public Cla1(T a) {this.a=a;}public T getData(){return a;}abstract void printInfo();
}
class Cla2<T,T2> extends Cla1<T> //Cla2繼承Cla1同時還可以增加泛型的個數,這里增加了T2
{T2 b;public Cla2(T a,T2 b) {super(a);this.b=b;}public T2 getDataB(){return b;}@Overridevoid printInfo() {//如果這個類也是抽象類的話,就不用在將父類里面的抽象方法給補全了System.out.println("繼承的類是泛型抽象類,所以要在子類里面實現抽象方法的方法體");}
}
public class Main {public static void main(String[] args) { Cla2<Integer,String> c2=new Cla2<Integer,String>(110,"String");System.out.println(c2.getData());System.out.println(c2.getDataB());c2.printInfo();}
泛型類可以實現繼承泛型接口:
interface Cla1<T>
{abstract void printfInfo(T t);//在接口中public和abstract可以省略,默認就是public和abstract
}
class Cla2<T> implements Cla1<T>
{public void printfInfo(T t){System.out.println("泛型類繼承泛型接口");}
}
public class Main {public static void main(String[] args) {Cla2<String> c1=new Cla2<String>();c1.printfInfo("");}
}
限制泛型可用類型:
- 在定義泛型類別的時候,默認在實例化泛型類的時候可以使用任何類型,但是如果要限制使用泛型類型時,只能用某個特定類型或者是其子類型才能實例化該類型時,可以在定義類型時,使用extends關鍵字指定這個類型必須是繼承某個類或者實現某個接口。例如:
class Cla11<T estends Move>
這里Move是接口interface move
這就要求T是接口Move類型的。接口是一個模版,需要類來繼承這個接口來實現才能實例化,所以說繼承了Move這個接口的類都屬于T extren Move。這里就要求是繼承了Move接口的類的類型。class Cla1<T extends Animal>
這里Animal是自己定義的一個類,那么T extends Animal
表示T只能是Animal類型或者其子類類型。 - 當沒有指定泛型繼承的類型或者接口時,默認使用
extends Object
,所以默認情況下任何類型都可以作為參數傳入。
限制使用泛型類型,只能用某個特定類型或者是其子類型才能實例化該類型:
interface Cla1<T extends String>
{abstract void printfInfo(T t);//在接口中public和abstract可以省略,默認就是public和abstract
}
class Cla2<T extends String> implements Cla1<T>//限制泛型只能用某個特定類型或者是其子類型使用extends繼承,限制使用泛型類型為String類型
{T str;public Cla2(T str) {this.str=str;}public void printfInfo(T t){System.out.println(str);System.out.println("泛型類繼承泛型接口");}
}
class Cla3<T extends String,T2 extends Integer > extends Cla2<T>
{T2 in;public Cla3(T str,T2 in) {super(str);this.in=in;}public void printfInfo(T t){System.out.println("父類:"+str+" 子類:"+in);}
}
public class Main {public static void main(String[] args) {Cla2<String> c1=new Cla2<String>("父類");c1.printfInfo(" ");Cla3<String,Integer> c2=new Cla3<String,Integer>("子類",10);c2.printfInfo(" ");}
}
類型通配符聲明:
- 同一泛型類,如果實例化時給定的實際類型不同,則這些實例的類型是不兼容的,不能相互賦值。
- 泛型類實例之間的不見兼容性會帶來使用的不方便。我們可以使用泛型通配符
<?>
聲明泛型類的變量就可以解決這個問題。 - 和限制泛型的上限相似,同樣可以使用
extends
關鍵字限定通配類型的上限:<? extends 為某種類型>
比如:<? extends String>
- 還可以使用super關鍵詞將通配符匹配類型限定為某個類型及其父類型
class Animal
{}
class Dog extends Animal
{}
class Cla1<T>
{T a;public Cla1(T a) {this.a=a;}T getData(){return a;}
}
public class Main {public static void main(String[] args) {Cla1<String> c1=new Cla1<String>("String");System.out.println(c1.getData());Cla1<Double> c2=new Cla1<Double>(1.23);System.out.println(c2.getData());//c1=c2;不允許這樣會報錯,因為不是一個類型的Cla1<?> c3;//使用通配符,即可不用限制類型c3=c1;c3=c2;Cla1<? extends String> c4;c4=c1;//c4=c2;因為限制了為String類型所以會報錯Cla1<Dog> c5=new Cla1<Dog>(new Dog());Cla1<Object> c7=new Cla1<Object>(new Object());Cla1<? super Dog> c6;//使用super關鍵詞將通配符匹配類型限定為某個類型及其父類型,c6必須是Dog的父類c6=c5;Cla1<? super Animal> c8;c8=c7;}
}
泛型方法:
- 不僅類可以聲明泛型,類中的方法也可以聲明僅用于自身的泛型,這種方法叫做泛型方法。其定義格式為:
訪問修飾符 <泛型列表> 返回類型 方法名(){}
- 在泛型列表中聲明的泛型,可用于該方法的返回類型聲明、參數類型聲明和方法代碼中的局部變量的類型聲明。
- 類中的其他方法不能使用當前方法聲明的泛型。
- 泛型方法更加靈活,它可以不受泛型類的約束。比方法重載更加靈活。
- 提示: 是否擁有泛型方法,與其所在的類是否是泛型類沒有關系,要定義泛型方法,只需要將泛型參數列表置于返回值前。
- 什么時候使用泛型方法,而不是泛型類呢? ① 添加類型約束條件只作用于一個方法的多個參數之間、而不涉及到類中的其他方法時。② 施加類型約束的方法為靜態方法,只能將其定義為泛型方法,因為靜態方法不能使用其所在類的類型參數。
- 注意: 同樣泛型方法也可以對齊進行限制和在泛型類里面的限制一樣。T在編譯的時候會重置為該類型在類中聲明是所繼承的父類。比如:
<T> T printInfo(T t)
T默認是繼承Object的,所以在編譯的時候會重置為Object,而<T extends Animal>
在編譯的時候會重置為Animal。
class Animal
{void eat(){System.out.println("動物吃");}
}
class Dog extends Animal
{void eat(){System.out.println("狗啃骨頭");}
}
class A<T>
{void printInfo(T t){System.out.println(t);}
}
class B
{<T> T printInfo(T t){//T僅對本方法有效,可用于該方法的返回類型聲明和該方法的參數類型聲明T a;//可用于方法代碼中的局部變量的類型聲明System.out.println(t);return t;}<T,T2> void printInfo(T t,T2 t2){//一個泛型方法有多個泛型參數,這也是個泛型方法的重載//System.out.println(t+t2);這樣寫不允許,必須分開寫,因為還沒有實例化,類型不確定,不能相加System.out.println(t);System.out.println(t2);}<T extends Animal> void printInfo2(T t)//施加泛型方法的類型約束{t.eat();}static <T extends Animal> void printInfo3(T t)//施加泛型方法的類型約束{t.eat();}
}
public class Main {public static void main(String[] args) {A<String> a=new A<String>();a.printInfo("String");B b=new B();b.printInfo("String");b.printInfo('c');b.printInfo(1213);b.printInfo(1,"str");b.printInfo2(new Dog());b.printInfo2(new Animal());B.printInfo3(new Animal());B.printInfo3(new Dog());//靜態方法本身就是和類是脫離的,可以直接通過類名來調用靜態方法}
}