Java泛型T,E,K,V,N,?與Object區(qū)別和含義
通常我們在看一些源碼時,發(fā)現(xiàn)全是T、?,暈乎乎的:sob:。于是,把泛型掌握好十分重要!
什么是泛型
Java 泛型(generics)是 JDK 5 中引入的一個新特性, 泛型提供了編譯時類型安全檢測機制,該機制允許程序員在編譯時檢測到非法的類型。
泛型的本質(zhì)是參數(shù)化類型,也就是說所操作的數(shù)據(jù)類型被指定為一個參數(shù)。
泛型有什么好處?寫個例子一目了然:
我們要封裝一個消息響應類:
public class Result implements Serializable { // 響應碼 Integer code; // 是否成功 Boolean success; // 返回體數(shù)據(jù) User user; public Result(Integer code, Boolean success, User user) { this.code = code; this.success = success; this.user = user; } @Override public String toString() { return "Result{" + "code=" + code + ", success=" + success + ", user=" + user + '}'; } public static void main(String[] args) { User user = new User(1, "Tony"); Result result = new Result(200, true, user); System.out.println(result); } } class User implements Serializable { Integer id; String name; public User(Integer id, String name) { this.id = id; this.name = name; } @Override public String toString() { return "User{" + "id=" + id + ", name='" + name + '\'' + '}'; } }
Result{code=200, success=true, user=User{id=1, name='Tony'}} 進程已結(jié)束,退出代碼0
呼~這樣這個反應體就可以返回請求狀態(tài)和用戶信息了??涩F(xiàn)在需求又需要返回關(guān)于手機的信息,那我們又得封裝一個能返回手機信息的響應類了...到后面還有衣服、鞋子...那不得累死?這時候泛型登場了:
public class Result<T> implements Serializable { // 響應碼 Integer code; // 是否成功 Boolean success; // 返回體數(shù)據(jù) T data; public Result(Integer code, Boolean success, T data) { this.code = code; this.success = success; this.data = data; } @Override public String toString() { return "Result{" + "code=" + code + ", success=" + success + ", data=" + data + '}'; } public static void main(String[] args) { User user = new User(1, "Tony"); Result<User> resultUser = new Result<>(200, true, user); System.out.println(resultUser); Phone phone = new Phone(999.99, "Yellow"); Result<Phone> resultPhone = new Result<>(200, true, phone); System.out.println(resultPhone); } } class User implements Serializable { Integer id; String name; public User(Integer id, String name) { this.id = id; this.name = name; } @Override public String toString() { return "User{" + "id=" + id + ", name='" + name + '\'' + '}'; } } class Phone implements Serializable { Double price; String color; @Override public String toString() { return "Phone{" + "price=" + price + ", color='" + color + '\'' + '}'; } public Phone(Double price, String color) { this.price = price; this.color = color; } }
Result{code=200, success=true, data=User{id=1, name='Tony'}} Result{code=200, success=true, data=Phone{price=999.99, color='Yellow'}} 進程已結(jié)束,退出代碼0
可見,利用泛型,可以統(tǒng)一標識需要返回的實體類。不管你來什么類,我都可以給你塞進去!
第一次接觸可能看不太明白,下面就詳細講解
泛型方法
你可以寫一個泛型方法,該方法在調(diào)用時可以接收不同類型的參數(shù)。根據(jù)傳遞給泛型方法的參數(shù)類型,編譯器適當?shù)靥幚砻恳粋€方法調(diào)用。
語法規(guī)則
所有泛型方法聲明都有一個類型參數(shù)聲明部分(由尖括號分隔),該類型參數(shù)聲明部分在方法返回類型之前
比如說這是一個用來打印數(shù)組的泛型方法:
private static <E> void printArray(E[] inputArray)
每一個類型參數(shù)聲明部分包含一個或多個類型參數(shù),參數(shù)間用逗號隔開。一個泛型參數(shù),也被稱為一個類型變量,是用于指定一個泛型類型名稱的標識符。
比如這個方法
private static <E,T> void printArray(E[] inputArray, T data)
類型參數(shù)能被用來聲明返回值類型,并且能作為泛型方法得到的實際參數(shù)類型的占位符。
泛型方法體的聲明和其他方法一樣。注意類型參數(shù)只能代表引用型類型,不能是原始類型(int double char等)
泛型標記符
- E Element 集合元素
- T Type Java類
- K Key 鍵
- V Value 值
- N Number 數(shù)值類型
- ? 表示不確定的Java類型
這些標記并不是限定只有對應的類型才能使用,即使你統(tǒng)一使用A-Z英文字母的其中一個,編譯器也不會報錯。之所以又不同的標記符,這是一種**約定。**在開發(fā)中很多規(guī)則都是一種約定,它能提高我們代碼的可讀性,方便團隊見的合作開發(fā)
寫個完整的例子:
public class TFunction { public static void main(String[] args) { // 創(chuàng)建各種類型的數(shù)組 Integer[] intArray = {1, 2, 3, 4, 5}; Double[] doubleArray = {1.1, 2.2, 3.3, 4.4}; Character[] charArray = {'H', 'E', 'L', 'L', 'O'}; System.out.println("整型數(shù)組元素為:"); printArray(intArray); // 傳遞一個整型數(shù)組 System.out.println("\n雙精度型數(shù)組元素為:"); printArray(doubleArray); // 傳遞一個雙精度型數(shù)組 System.out.println("\n字符型數(shù)組元素為:"); printArray(charArray); // 傳遞一個字符型數(shù)組 } // 泛型方法 private static <E> void printArray(E[] inputArray) { // 遍歷打印數(shù)組 Arrays.stream(inputArray).forEach(e -> { System.out.printf("%s ", e); }); System.out.println(); } }
泛型類
泛型類的聲明與非泛型類幾乎相同,唯一的不同在于類名的后面添加了參數(shù)聲明部分
這邊就不舉例子了,因為開篇的例子就是封裝了一個泛型類,當時不太理解的可以再回去看一下
類型通配符
?
我們一般可以使用?來承接所有的引用類型,搬運一個菜鳥上的例子:
public class GenericTest { public static void main(String[] args) { List<String> name = new ArrayList<String>(); List<Integer> age = new ArrayList<Integer>(); List<Number> number = new ArrayList<Number>(); name.add("icon"); age.add(18); number.add(314); getData(name); getData(age); getData(number); } public static void getData(List<?> data) { System.out.println("data :" + data.get(0)); } }
data :icon data :18 data :314
?extends T
這是**泛型上邊界:**只有T對象的子類可以被傳入
如果是? extends C,那么只有D和E允許被傳入,否則會編譯報錯
? super T
這是**泛型下邊界:**只有T對象的父類可以被傳入
如果是? super D,那么只有C和A允許被傳入,否則會編譯報錯
T 和 ?
不知道看到這里,有沒有疑惑。T和?好像作用差不多啊,有什么區(qū)別?
這里解釋一下,T一般作為泛型參數(shù),而?是更多是用來一個不確定的引用類型,意會一下吧~~~
T 和 Object
重頭戲??!
知道的Object的同志都了解其是Java的超類(所有對象的父類),不了解的可以去看看我的博客,有做詳細的解釋。
那么問題就來了,Object好像可以代替泛型的功能??!所有能用到泛型的地方Object都可以!
其實,在JDK5之前,都是用的Object,但其存在很多的問題,JDK5之后便引入了泛型
Object是所有類的父類,在編碼過程中就難免出現(xiàn)類型轉(zhuǎn)化問題,且在編譯階段不會報錯,到了運行階段才暴露問題,大大降低了程序的安全性和健壯性!
舉例之前說一些轉(zhuǎn)型的分類:
向上轉(zhuǎn)型 用父類聲明一個子類對象 例如:Animal是Cat的父類,在聲明時我們這么寫:
Animal cat = new Cat();
向下轉(zhuǎn)型
將父類對象強轉(zhuǎn)為其子類實例:
Animal cat = new Cat(); Cat anotherCat = (Cat) cat;
所以當我們使用Object作為泛型來使用時,不僅寫起來麻煩,還要不停的進行類型轉(zhuǎn)化,還很容易出現(xiàn)問題,很遜的誒~
舉個例子看看:
利用Object定義了一個數(shù)字變量,我們常識將其向下轉(zhuǎn)型為Integer和String。將一個數(shù)字轉(zhuǎn)型為字符串是一件荒唐的事情,可編譯器并不能察覺這件事,直到程序運行了起來...
類型轉(zhuǎn)換異常!??!
總結(jié)
泛型的出現(xiàn),當類型轉(zhuǎn)化出現(xiàn)問題的時候,在編譯階段就會暴露出來。解決了Object存在的諸多問題,讓代碼更加優(yōu)雅,程序更加安全,更加健壯。
以上就是Java泛型T,E,K,V,N,?與Object區(qū)別和含義的詳細內(nèi)容,更多關(guān)于Java 泛型的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Java并發(fā)編程創(chuàng)建并運行線程的方法對比
這篇文章主要為大家詳細介紹了Java并發(fā)編程創(chuàng)建并運行線程的方法,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來幫助2022-03-03Java中StringBuilder與StringBuffer使用及源碼解讀
我們前面學習的String就屬于不可變字符串,因為理論上一個String字符串一旦定義好,其內(nèi)容就不可再被改變,但實際上,還有另一種可變字符串,包括StringBuilder和StringBuffer兩個類,那可變字符串有什么特點,又怎么使用呢,接下來就請大家跟我一起來學習吧2023-05-05解決springboot讀取application.properties中文亂碼問題
初用properties,讀取java properties文件的時候如果value是中文,會出現(xiàn)亂碼的問題,所以本文小編將給大家介紹如何解決springboot讀取application.properties中文亂碼問題,需要的朋友可以參考下2023-11-11