JAVA深化篇_08——搞懂java泛型全部知識點只需這一篇博文
泛型(Generics)
在開始介紹泛型之前,先淺淺聊點題外話,相信小伙伴們看到了封面的庫男神,NBA新賽季馬上開始,希望庫男神帶領(lǐng)新一代五小勇士重回巔峰!!!好了,接下來我們開始我們的正式話題,相信兄弟們更多的還是為了學(xué)習(xí)哈哈!!
泛型基本概念
泛型是JDK5.0以后增加的新特性。
泛型的本質(zhì)就是==“數(shù)據(jù)類型的參數(shù)化”,處理的數(shù)據(jù)類型不是固定的,而是可以作為參數(shù)傳入。我們可以把“泛型”理解為數(shù)據(jù)類型的一個占位符(類似:形式參數(shù)),即告訴編譯器,在調(diào)用泛型時必須傳入實際類型==
參數(shù)化類型,白話說就是:
- 把類型當(dāng)作是參數(shù)一樣傳遞。
- <數(shù)據(jù)類型> 只能是引用類型。<基本數(shù)據(jù)類型用它的包裝類>
泛型的好處
在不使用泛型的情況下,我們可以使用Object類型來實現(xiàn)任意的參數(shù)類型,但是在使用時需要我們強制進行類型轉(zhuǎn)換。這就要求程序員明確知道實際類型,不然可能引起類型轉(zhuǎn)換錯誤;但是,在編譯期我們無法識別這種錯誤,只能在運行期發(fā)現(xiàn)這種錯誤。使用泛型的好處就是可以在編譯期就識別出這種錯誤,有了更好的安全性;同時,所有類型轉(zhuǎn)換由編譯器完成,在程序員看來都是自動轉(zhuǎn)換的,提高了代碼的可讀性。
總結(jié)一下,就是使用泛型主要是兩個好處:
- 代碼可讀性更好【不用強制轉(zhuǎn)換】
- 程序更加安全【只要編譯時期沒有警告,運行時期就不會出現(xiàn)ClassCastException異常】
類型擦除
編碼時采用泛型寫的類型參數(shù),編譯器會在編譯時去掉,這稱之為“類型擦除”。
泛型主要用于編譯階段,編譯后生成的字節(jié)碼class文件不包含泛型中的類型信息,涉及類型轉(zhuǎn)換仍然是普通的強制類型轉(zhuǎn)換。類型參數(shù)在編譯后會被替換成Object,運行時虛擬機并不知道泛型。
泛型主要是方便了程序員的代碼編寫,以及更好的安全性檢測。
泛型類
泛型標記
定義泛型時,一般采用幾個標記:E、T、K、V、N、?。他們約定俗稱的含義如下:
| 泛型標記 | 對應(yīng)單詞 | 說明 |
|---|---|---|
| E | Element | 在容器中使用,表示容器中的元素 |
| T | Type | 表示普通的JAVA類 |
| K | Key | 表示鍵,例如:Map中的鍵Key |
| V | Value | 表示值 |
| N | Number | 表示數(shù)值類型 |
| ? | 表示不確定的JAVA類型 |
泛型類的使用
語法結(jié)構(gòu)
public class 類名<泛型標識符號> {
}
public class 類名<泛型標識符號,泛型標識符號> {
}
示例
public class Generic<T> {
private T flag;
public void setFlag(T flag){
this.flag = flag;
}
public T getFlag(){
return this.flag;
}
}
public class Test {
public static void main(String[] args) {
//創(chuàng)建對象時,指定泛型具體類型。
Generic<String> generic = new Generic<>();
generic.setFlag("admin");
String flag = generic.getFlag();
System.out.println(flag);
//創(chuàng)建對象時,指定泛型具體類型。
Generic<Integer> generic1 = new Generic<>();
generic1.setFlag(100);
Integer flag1 = generic1.getFlag();
System.out.println(flag1);
}
}
泛型接口
泛型接口和泛型類的聲明方式一致。
泛型接口的使用
語法結(jié)構(gòu)
public interface 接口名<泛型標識符號> {
}
public interface 接口名<泛型標識符號,泛型標識符號> {
}
示例
public interface IGeneric<T> {
T getName(T name);
}
//在實現(xiàn)接口時傳遞具體數(shù)據(jù)類型
public class IgenericImpl implements Igeneric<String> {
@Override
public String getName(String name) {
return name;
}
}
//在實現(xiàn)接口時仍然使用泛型作為數(shù)據(jù)類型
public class IGenericImpl2<T> implements IGeneric<T>{
@Override
public T getName(T name) {
return name;
}
}
public class Test {
public static void main(String[] args) {
IGeneric<String> igeneric= new IGenericImpl();//在實例化泛型接口的實現(xiàn)類對象時,給定泛型的具體數(shù)據(jù)類型
String name = igeneric.getName("oldlu");
System.out.println(name);
IGeneric<String> igeneric1 = new IGenericImpl2<>();
String name1 = igeneric1.getName("itbz");
System.out.println(name1);
}
}
泛型方法
類上定義的泛型,在方法中也可以使用。但是,我們經(jīng)常需要僅僅在某一個方法上使用泛型,這時候可以使用泛型方法。
調(diào)用泛型方法時,不需要像泛型類那樣告訴編譯器是什么類型,編譯器可以自動推斷出類型
泛型方法的使用
非靜態(tài)方法
非靜態(tài)方法可以使用泛型類中所定義的泛型,也可以將泛型定義在方法上。
語法結(jié)構(gòu)
//無返回值方法
public <泛型標識符號> void getName(泛型標識符號 name){
}
//有返回值方法
public <泛型標識符號> 泛型標識符號 getName(泛型標識符號 name){
}
示例
public class MethodGeneric {
public <T> void setName(T name){
System.out.println(name);
}
public <T> T getAge(T age){
return age;
}
}
public class Test2 {
public static void main(String[] args) {
MethodGeneric methodGeneric = new MethodGeneric();
methodGeneric.setName("oldlu");
Integer age = methodGeneric.getAge(123);
System.out.println(age);
}
靜態(tài)方法
靜態(tài)方法中使用泛型時有一種情況需要注意一下,那就是靜態(tài)方法無法訪問類上定義的泛型,所以必須要將泛型定義在方法上。
語法結(jié)構(gòu)
//無返回值靜態(tài)方法
public static <泛型標識符號> void setName(泛型標識符號 name){
}
//有返回值靜態(tài)方法
public static <泛型標識符號> 泛型表示符號 getName(泛型標識符號 name){
}
示例
public class MethodGeneric {
public static <T> void setFlag(T flag){
System.out.println(flag);
}
public static <T> T getFlag(T flag){
return flag;
}
}
public class Test4 {
public static void main(String[] args) {
MethodGeneric.setFlag("oldlu");
Integer flag1 = MethodGeneric.getFlag(123123);
System.out.println(flag1);
}
}
泛型方法與可變參數(shù)
在泛型方法中,泛型也可以定義可變參數(shù)類型。
語法結(jié)構(gòu)
public <泛型標識符號> void showMsg(泛型標識符號... agrs){
}
示例
public class MethodGeneric {
public <T> void method(T...args){
for(T t:args){
System.out.println(t);
}
}
}
泛型中的通配符<?>
無界通配符
“?”表示類型通配符,用于代替具體的類型。它只能在“<>”中使用。可以解決當(dāng)具體類型不確定的問題。
語法結(jié)構(gòu)
public void showFlag(Generic<?> generic){
}
示例
public class Generic<T> {
private T flag;
public void setFlag(T flag){
this.flag = flag;
}
public T getFlag(){
return this.flag;
}
}
public class ShowMsg {
public void showFlag(Generic<?> generic){
System.out.println(generic.getFlag());
}
}
public class Test3 {
public static void main(String[] args) {
ShowMsg showMsg = new ShowMsg();
Generic<Integer> generic = new Generic<>();
generic.setFlag(20);
showMsg.showFlag(generic);
Generic<Number> generic1 = new Generic<>();
generic1.setFlag(50);
showMsg.showFlag(generic1);
Generic<String> generic2 = new Generic<>();
generic2.setFlag("oldlu");
showMsg.showFlag(generic2);
}
}
統(tǒng)配符的上下限定
統(tǒng)配符的上限限定
對通配符的上限的限定:<? extends 類型>
?實際類型可以是上限限定中所約定的類型,也可以是約定類型的子類型;
語法結(jié)構(gòu)
public void showFlag(Generic<? extends Number> generic){
}
示例
public class ShowMsg {
public void showFlag(Generic<? extends Number> generic){
System.out.println(generic.getFlag());
}
}
public class Test4 {
public static void main(String[] args) {
ShowMsg showMsg = new ShowMsg();
Generic<Integer> generic = new Generic<>();
generic.setFlag(20);
showMsg.showFlag(generic);
Generic<Number> generic1 = new Generic<>();
generic1.setFlag(50);
showMsg.showFlag(generic1);
}
}
通配符的下限限定
對通配符的下限的限定:<? super 類型>
?實際類型可以是下限限定中所約定的類型,也可以是約定類型的父類型;
語法結(jié)構(gòu)
public void showFlag(Generic<? super Integer> generic){
}
示例
public class ShowMsg {
public void showFlag(Generic<? super Integer> generic){
System.out.println(generic.getFlag());
}
}
public class Test6 {
public static void main(String[] args) {
ShowMsg showMsg = new ShowMsg();
Generic<Integer> generic = new Generic<>();
generic.setFlag(20);
showMsg.showFlag(generic);
Generic<Number> generic1 = new Generic<>();
generic1.setFlag(50);
showMsg.showFlag(generic1);
}
}
泛型局限性和常見錯誤
泛型主要用于編譯階段,編譯后生成的字節(jié)碼class文件不包含泛型中的類型信息。 類型參數(shù)在編譯后會被替換成Object,運行時虛擬機并不知道泛型。因此,使用泛型時,如下幾種情況是錯誤的:
-
基本類型不能用于泛型<泛型必須為引用數(shù)據(jù)類型>
Test<int> t;這樣寫法是錯誤,我們可以使用對應(yīng)的包裝類Test<Integer> t ; -
不能通過類型參數(shù)創(chuàng)建對象
T elm = new T();運行時類型參數(shù)T會被替換成Object,無法創(chuàng)建T類型的對象,容易引起誤解,java干脆禁止這種寫法。
浙公網(wǎng)安備 33010602011771號