前言
最近一段時間經常研究 Java 原始碼,在原始碼中經常可以看到泛型的影子,看到了泛型就想到了我以前那張懵逼的臉😮 什么是泛型?泛什么型?什么泛型?
一提到泛型,可能很多小伙伴都知道它,但是并不了解它,正好今天我就和大家分享一下我對泛型的理解,聽小弟娓娓道來~ ?
什么是泛型
百度上有很多關于泛型的解釋,但是描述的內容都不是很好理解,俗話說“繁瑣問題必有猥瑣解法”,那么泛型的“猥瑣”解法就來了👇
“泛型”,通過字面可以理解為廣泛的型別(這個型別可以應用在類、介面、方法,所以說他廣泛),既然它是廣泛的型別,那么就保證了代碼與它們能夠操作的資料型別是拆分開的,并不會系結在一起,這也就可以讓同一套代碼用于多種資料型別,使用泛型的好處就是保證了代碼的復用性,降低了耦合度,當然也就提升了代碼的可讀性,
下面咱們結合一段代碼來看看:
import java.util.ArrayList;
import java.util.List;
/**
* 深入淺出聊 Java 泛型
* @description: GenericDemo
* @author: 莊霸.liziye
* @create: 2021-12-20 10:47
**/
public class GenericDemo {
public static void main(String[] args) {
List arrayList = new ArrayList();
arrayList.add("我在人民廣場吃著炸雞");
arrayList.add("賣炸雞花了");
arrayList.add(20);
for(int i = 0; i< arrayList.size();i++){
String item = (String)arrayList.get(i);
System.out.println("item = " + item);
}
}
}
不出意外,這段代碼拋出例外了~

咱們這時候還沒有指定 List 集合的型別,如果將 List 集合指定成 String、或者 Integer 型別的話,那么 IDEA 就直接提示代碼存在問題了👇

所以在這種場景下,就需要用上泛型了~ 接下來就分別簡單說說泛型類、泛型介面和泛型方法🍺
泛型類
咱們先寫一個簡單的泛型類 + 測驗方法👇
/**
* 簡單泛型類
* @description: Generic
* @author: 莊霸.liziye
* @create: 2021-12-20 11:33
**/
public class Generic<T>{
private T value;
public Generic(T value) {
this.value = value;
}
public T getValue(){
return value;
}
}
/**
- 深入淺出聊 Java 泛型
- @description: GenericDemo
- @author: 莊霸.liziye
- @create: 2021-12-20 10:47
**/
public class GenericDemo {
public static void main(String[] args) {
Generic<Integer> genericInteger = new Generic<Integer>(20);
Generic<String> genericString = new Generic<String>("我在人民廣場吃著炸雞");
System.out.println("genericInteger = " + genericInteger.getValue());
System.out.println("genericString = " + genericString.getValue());
}
}
咱們執行一下測驗方法,看看執行結果是什么樣子的~

這里有幾點需要注意的是:
- 泛型類中,T 可以隨便寫為任意標識,常見的標識有 T(Type)、E(Element)、K(Key)、V(Value) 等,
- 泛型的型別引數只能是型別別(型別別說白了就是 Class 型別),不能是簡單型別(如 int 等),
- 使用泛型類的時候,不一定要傳入泛型型別實參,當傳入非泛型型別實參時,泛型類中使用泛型的方法或成員變數定義的型別可以為任何型別,舉個小例子👇

泛型介面
直接上代碼?
/**
* 深入淺出聊 Java 泛型
* @description: GenericDemo
* @author: 莊霸.liziye
* @create: 2021-12-20 10:47
**/
public class GenericDemo {
public static void main(String[] args) {
IMessage<String> message = new MessageImpl<String>();
message.printMessage("我在人民廣場吃著炸雞");
}
}
/**
* 定義泛型介面
**/
interface IMessage<T>{
public void printMessage(T t);
}
/**
* 實作泛型介面
**/
class MessageImpl<T> implements IMessage<T>{
@Override
public void printMessage(T t) {
System.out.println(t);
}
}
上面的代碼是在實作泛型介面的類時未傳入泛型實參的情況,在這種情況下有一點是需要注意的:在宣告類的時候,需將泛型的宣告也加在介面實作類中(class MessageImpl < T > implements IMessage< T >),如果在介面實作類中未宣告泛型的話,編輯器則會直接報錯,如下圖所示:

咱們改造一下代碼,再來看看第二種情況~
/**
* 深入淺出聊 Java 泛型
* @description: GenericDemo
* @author: 莊霸.liziye
* @create: 2021-12-20 10:47
**/
public class GenericDemo {
public static void main(String[] args) {
IMessage<String> message =new MessageImpl();
message.printMessage("我在人民廣場吃著炸雞");
}
}
/**
* 定義泛型介面
**/
interface IMessage<T>{
public void printMessage(T t);
}
/**
* 實作泛型介面
**/
class MessageImpl implements IMessage<String>{
@Override
public void printMessage(String t) {
System.out.println(t);
}
}
這次我們傳入了泛型實參,雖然我們只創建了一個泛型介面,但是我們可以為T傳入無數個實參,形成無數種型別的泛型介面,此時有一點是需要特別注意:當泛型型別傳入了實參型別時,則所有使用泛型的地方都要替換成傳入的實參型別,
泛型方法
/**
* 深入淺出聊 Java 泛型
* @description: GenericDemo
* @author: 莊霸.liziye
* @create: 2021-12-20 10:47
**/
public class GenericDemo {
public static void main(String[] args) {
//顯式賦值
String p1 = GenericDemo.<String>printfArray("我在人民廣場吃著炸雞");
//隱式賦值,常用此方式,可以不指定 <String>
String p2 = GenericDemo.printfArray("我在人民廣場吃著炸雞");
System.out.println("p1 = " + p1);
System.out.println("p2 = " + p2);
}
/**
* 泛型方法
* @param message
* @param <T>
* @return
*/
public static <T> T printfArray(T message) {
return message;
}
}
關于泛型方法,這里就需要多說幾句了👇
① 泛型方法在呼叫方法的時候指明泛型的具體型別,和泛型類剛好相反(泛型類在實體化類的時候指明泛型的具體型別),
②在上面寫的泛型方法中,static 與 T(即回傳值型別)中間 < T > 非常重要,可以理解為這就是泛型方法的一個標記,只有宣告了< T > 的方法才是泛型方法,但是,在泛型類中的使用了泛型的成員方法并不是泛型方法,
用上面定義的泛型類舉個例子👀
/**
* 簡單泛型類
* @description: Generic
* @author: 莊霸.liziye
* @create: 2021-12-20 11:33
**/
public class Generic<T>{
private T value;
public Generic(T value) {
this.value = value;
}
public T getValue(){
return value;
}
}
在泛型類中定義了一個 getValue() 方法,有些小伙伴可能會覺得 getValue() 方法是一個泛型方法,其實不然,雖然在方法中使用了泛型,但 getValue() 方法只是一個普通的成員方法,只不過他的回傳值型別用的是宣告泛型類時宣告的泛型,
③ 與泛型類的定義一樣,此處的 T 可以隨便寫為任意標識,常見的如T、E、K、V等形式的引數常用于表示泛型,


泛型的邊界
在使用泛型的時候,我們還可以為傳入的泛型型別實參進行邊界的限制,
/**
* 簡單泛型類
* @description: Generic
* @author: 莊霸.liziye
* @create: 2021-12-20 11:33
**/
public class Generic<T extends Number>{
private T value;
public Generic(T value) {
this.value = value;
}
public T getValue(){
return value;
}
}
先改造一下泛型類,為泛型添加邊界,限制傳入的型別實參必須是 Number (或其他)型別的子類,如果此時我們傳入的引數型別是 String ,那么編輯器則會提示錯誤(String 型別并不是 Number 型別的子類)👇

泛型的擦除機制
通過上面的例子,相信大家也對泛型有了一個更深的理解,最后再簡單說一下泛型的擦除機制,
import java.util.ArrayList;
import java.util.List;
/**
* 深入淺出聊 Java 泛型
* @description: GenericDemo
* @author: 莊霸.liziye
* @create: 2021-12-20 10:47
**/
public class GenericDemo {
public static void main(String[] args) {
List<String> stringArrayList = new ArrayList<String>();
List<Integer> integerArrayList = new ArrayList<Integer>();
Class classStringArrayList = stringArrayList.getClass();
Class classIntegerArrayList = integerArrayList.getClass();
System.out.println(classStringArrayList == classIntegerArrayList);
}
}

我們明明宣告的是兩個型別不同的 List 集合,為什么列印出來的結果是 true 呢?
這就是泛型的擦除機制,在運行程序中任何和型別有關的資訊都會被擦除,在運行程序中中,ArrayList< String > 和 ArrayList< Integer > 的具體資訊都被擦除成它們的原生型別也就是 ArrayList 型別,也就是說泛型的型別在邏輯上可以看成是多個不同的型別,實際上都是一樣的,
再說的具體點:玩英雄聯盟的時候,我們選擇了“劍圣”這個英雄,無論我們使用了“暗影·易”、“天人合一·易”還是“源計劃·易”,他只是看起來不一樣了,其實它依然是“劍圣”這個英雄,
最后再說一句:在研究原始碼的時候會發現,泛型使用頻率最高的場景就是在定義集合的時候,所以在我們日常的編碼程序中,合理的使用泛型去定義集合可以大大的簡化編碼,同時還可以保證我們的代碼有一個良好的可讀性和可用性👍,
小結
本人經驗有限,有些地方可能講的沒有特別到位,如果您在閱讀的時候想到了什么問題,歡迎在評論區留言,我們后續再一一探討🙇?
希望各位小伙伴動動自己可愛的小手,來一波點贊+關注 (????) 讓更多小伙伴看到這篇文章~ 蟹蟹呦(●’?’●)
如果文章中有錯誤,歡迎大家留言指正;若您有更好、更獨到的理解,歡迎您在留言區留下您的寶貴想法,
你在被打擊時,記起你的珍貴,抵抗惡意;
你在迷茫時,堅信你的珍貴,拋開蜚語;
愛你所愛 行你所行 聽從你心 無問東西
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/388361.html
標籤:java
上一篇:將MenuItem.IsEnabled系結到視圖模型的屬性
下一篇:如何撰寫類似不和諧游戲活動的程式
