十九、泛型的使用
1、泛型引入背景
Java集合不會知道它們需要用它來儲存什麼型別的物件,所以他們把集合設計成能儲存任何型別的物件,只要求具有很好的通用性。就是因為集合對元素型別沒有任何限制,這樣回引發下列的問題:
例如想建立一個只能儲存Order物件的集合,但程式也可以輕易地將User物件“丟”進去,所以可能引發異常。由於把物件“丟進”集合時,集合丟失了物件的狀態資訊,集合只知道它盛裝的是Object,因此取出集合元素後通常還需要進行強制型別轉換。這種強制型別轉換既會增加程式設計的複雜度、也可能引發ClassCastException。
因此JDK1.5改寫了集合框架中的全部介面和類,為這些介面、類增加了泛型支援,從而可以在宣告集合變數、建立集合物件時傳入型別實參。
2、泛型含義:
1)為建立的泛型類定義的構造器時,構造器名還是原來的類名,不要增加泛型宣告。例如,為 GeneralClass<T>類定義構造器時,其構造器還是 GeneralClass,而不是 GeneralClass<T>。
2)呼叫構造器時,卻可以使用GeneralClass<T>的形式,當然應該為T形參傳入實際的型別引數。Java 7提供了菱形語法,允許省略<>中的型別實參。
泛型本質是引數化型別,也就是說變數的型別是一個引數,在使用時再指定為具體型別。泛型可以用於類、介面、方法,通過使用泛型可以使程式碼更簡單、安全。然而 Java 中的泛型使用了型別擦除,所以只是偽泛型。
3、泛型機制必要性及原理解析
泛型程式設計(Generic Programming):“使用泛型機制編寫的程式程式碼要比那些雜亂地使用Object變數,然後再進行強制型別轉換的程式碼具有更好的安全性和可讀性。泛型對於集合類尤其有用。”
4、Java泛型中的標記符含義:
E - Element (在集合中使用,因為集合中存放的是元素)
T - Type(Java 類)
K - Key(鍵)
V - Value(值)
N - Number(數值型別)
? - 表示不確定的java型別
5、萬用字元(Wildcards)可以在泛型例項化時更加靈活地控制,也可以在方法中控制方法的引數。
語法如下:
示例1:泛型類名<? extends T>
? extends T:表示T或T的子類
示列2:泛型類名<? super T>
? super T:表示T或T的父類
示例3:泛型類名<?>
?:表示可以是任意型別
6、泛型的具體應用
6.1泛型類的定義:[訪問修飾符] class 類名稱<T>
泛型類的主要作用在於類被例項化後,傳入具體的型別引數,對類的成員屬性的型別和成員方法的引數型別和返回值型別進行替換。
1)建立的泛型類,為該類定義的構造器時,構造器名還是原來的類名,不要增加泛型宣告。例如,為 GeneralClass<T>類定義構造器時,其構造器還是 GeneralClass,而不是 GeneralClass<T>。
2)呼叫構造器時,卻可以使用 GeneralClass<T>的形式,當然應該為T形參傳入實際的型別引數。Java 7提供了菱形語法,允許省略<>中的型別實參。程式碼示例如下:
public class GeneralClass<T> { T data; GeneralClass(T t) { data = t; } public T getData() { return data; } public void print() { System.out.println("base print : " + data); } public static void main(String[] args) { GeneralClass<String> generic1 = new GeneralClass<>("測試1"); System.out.println(generic1.getData()); GeneralClass generic2 = new GeneralClass<>("測試2"); System.out.println(generic2.getData()); GeneralClass<Double> generic3 = new GeneralClass<Double>(9999.7); System.out.println(generic3.getData()); AnimalDTO animalDTO=new AnimalDTO(); animalDTO.setUserName("動物"); GeneralClass generic4=new GeneralClass(animalDTO); System.out.println(generic4.getData()); } }View Code
6.2泛型方法定義:[public] [static] <T> 返回值型別 方法名(T 引數列表)
使用泛型方法時,至少返回值或引數有一個是泛型定義的,而且應該保持一致,否則可能會受到各種限制,因此,這裡建議保持一致。程式碼示例如下:
public class GeneralMethod { public static void main(String[] args) { GeneralMethod generalMethod= new GeneralMethod(); generalMethod.getClassSimpleName(new Integer(0),new String("generic")); } /** * @Method: * @Author: * @Description: 泛型方法 * GeneralMethod 類本身不是泛型的,建立它的物件的時候不需要傳入泛型引數,但是它的方法 getClassSimpleName 是泛型方法。在返回型別之前是它的引數標識 <K,V>,注意這裡有兩個泛型引數,所以泛型引數可以有多個。呼叫泛型方法時可以不顯式傳入泛型引數,上面的呼叫就沒有。這是因為編譯器會使用引數型別推斷,根據傳入的實參的型別 (這裡是 integer 和 String) 推斷出 K 和 V 的型別。 * * param: * @Return: * @Exception: * @Date: 2020/12/9 14:51 */ public <K,V> void getClassSimpleName(K k,V v) { System.out.println(k.getClass().getSimpleName()); System.out.println(v.getClass().getSimpleName()); } }View Code
6.3泛型類的繼承:程式碼示例如下
public class Father<E> { E e; } public class Son<T,T1> extends Father<T>{ /* 子類Son在定義的時候,如果省略了Father後<T>,那麼Son的T自動變成了Object,建議定義時加入<T>以保留父類的型別引數。Son類還可以增加新的泛型T1。 */ public static void main(String[] args){ System.out.println("Son>>>執行"); } }View Code
6.4萬用字元在泛型中的使用:程式碼示例如下
public class WildcardsTest2 { public static void main(String[] args) { List<? super Class1> list = new ArrayList<Class1>(); // 對於List<? super Class1> list,可以指向ArrayList<T1>和ArrayList<Class1>的引用,但是進行add操作時,只能新增Class1和Class2 list.add(new Class1()); list.add(new Class2()); List<BaseClass> list1 = new ArrayList<BaseClass>(); List<Class1> list2 = new ArrayList<Class1>(); List<Class2> list3 = new ArrayList<Class2>(); f(list1); f(list2); f(list3); } //只要是class2 的父類都可以 public static void f(List<? super Class2> list) { System.out.println("list>>>" + list); } } class BaseClass { } class Class1 extends BaseClass { } class Class2 extends Class1 { }View Code
7、程式碼開發中使用泛型,建立公共返回物件
public class ResponseDTO<T> implements Serializable { private boolean success; private String errorCode; /** * 原因 */ private String errorMsg; /** * 返回資料值 */ private T data; public ResponseDTO() { } public ResponseDTO(ErrorCodeEnum errorCode) { this.errorCode = errorCode.getErrorCode(); this.errorMsg = errorCode.getErrorMsg(); if(!"0".equals(errorCode.getErrorCode())){ success=false; }else{ success=true; } } public ResponseDTO(String errorCode, String errorMsg) { this.errorCode = errorCode; this.errorMsg = errorMsg; if(!"0".equals(errorCode)){ success=false; }else{ success=true; } } public void setErrorCodeEnum(BaseEnum errorCode) { this.errorCode = errorCode.getErrorCode(); this.errorMsg = errorCode.getErrorMsg(); if(!"0".equals(errorCode.getErrorCode())){ success=false; }else{ success=true; } } public String getErrorCode() { return errorCode; } public void setErrorCode(String errorCode) { this.errorCode = errorCode; } public String getErrorMsg() { return errorMsg; } public void setErrorMsg(String errorMsg) { this.errorMsg = errorMsg; } public T getData() { return data; } public void setData(T data) { this.data = data; } public boolean isSuccess() { return success; } public void setSuccess(boolean success) { this.success = success; } @Override public String toString() { return "ResponseDTO{" + "errorCode='" + errorCode + '\'' + ", errorMsg='" + errorMsg + '\'' + ", data=" + data + '}'; } } public interface BaseEnum { public String getErrorCode(); public String getErrorMsg(); } public enum ErrorCodeEnum implements BaseEnum{ OK("0", "成功"), FAIL_500("500", "系統開小差了,請稍後再試!"), FAIL_501("501", "服務異常,請聯絡管理員處理!"), PARAM_ERROR("502", "入參異常,請檢查後重試!"); private String errorCode; private String errorMsg; ErrorCodeEnum(String errorCode, String errorMsg) { this.errorCode = errorCode; this.errorMsg = errorMsg; } ErrorCodeEnum(BaseEnum errorCodeEnum) { this.errorCode = errorCodeEnum.getErrorCode(); this.errorMsg = errorCodeEnum.getErrorMsg(); } public String getErrorCode() { return errorCode; } public String getErrorMsg() { return errorMsg; } }View Code
參看文章
https://www.jb51.net/article/109033.htm
https://www.jb51.net/article/166703.htm