Java泛型詳解,通俗易懂只需5分鐘
點(diǎn)擊關(guān)注公眾號(hào),Java干貨及時(shí)送達(dá)

Java技術(shù)迷?|?出品
從Java1.5開(kāi)始,官方引入了泛型機(jī)制,事實(shí)上,從1.3版本開(kāi)始,泛型就已經(jīng)存在了,經(jīng)過(guò)歷代的發(fā)展,已足具雛形,本篇文章就來(lái)學(xué)習(xí)一下泛型的使用。
01
認(rèn)識(shí)泛型
在此之前,我們先來(lái)認(rèn)識(shí)一下泛型吧,先看一段示例:
public class Main {
public static void main(String[] args) {
List list = new ArrayList();
list.add(1);
list.add(2);
list.add(3);
dealList(list);
}
public static void dealList(List list) {
int result = 0;
for (Object obj : list) {
int num = (int) obj;
result += num;
}
System.out.println(result);
}
}
在該示例中,dealList方法用于計(jì)算一個(gè)集合中的元素和,當(dāng)然,只有數(shù)字才能夠參與運(yùn)算,但是示例中的list是可以存放任意對(duì)象的,所以很可能會(huì)出現(xiàn)如下情況:
public static void main(String[] args) {
List list = new ArrayList();
list.add(1);
list.add(2);
list.add(3);
list.add("4");
dealList(list);
}
因?yàn)樽址疅o(wú)法轉(zhuǎn)為整型,所以程序會(huì)報(bào)錯(cuò):
Exception in thread "main" java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Integer
這時(shí)候泛型的約束就顯得尤為重要,在定義集合的時(shí)候?qū)ζ溥M(jìn)行類型的限定即可在編譯期就避免這一類問(wèn)題:
02
定義泛型類
泛型的用法十分多樣,它可以作用在類、方法上實(shí)現(xiàn)各種功能,先來(lái)了解一下泛型類的定義。
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Genericity {
private String str1;
private String str2;
}
以上是一個(gè)簡(jiǎn)單的Java類,我們想通過(guò)它計(jì)算其中兩個(gè)屬性值的和:
public static void main(String[] args) {
Genericity genericity = new Genericity("1","1");
String str1 = genericity.getStr1();
String str2 = genericity.getStr2();
Integer num1 = Integer.valueOf(str1);
Integer num2 = Integer.valueOf(str2);
int result = num1 + num2;
System.out.println(result);
}
但此時(shí)我覺(jué)得將屬性定義為String是一個(gè)錯(cuò)誤的決定,因?yàn)橛?jì)算屬性值的和還需要自己對(duì)類型進(jìn)行轉(zhuǎn)換,所以我將屬性調(diào)整為了Integer類型:
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Genericity {
private Integer str1;
private Integer str2;
}
當(dāng)然了,這個(gè)例子可能有一些牽強(qiáng),事實(shí)上不會(huì)有人這么做,但目的很明顯,就是想讓類中的屬性類型可變,使得整個(gè)類更加靈活,基于此需求,我們可以使用泛型對(duì)其進(jìn)行改造:
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Genericity {
private T t1;
private T t2;
}
這就是泛型類的定義,通過(guò)在類名后面添加符號(hào)即可定義泛型類,而類中的屬性類型均為T,這將導(dǎo)致類中的屬性類型會(huì)跟隨T的變化而變化,用法如下:
public static void main(String[] args) {
Genericity<Integer> genericity = new Genericity<>(1, 1);
Integer t1 = genericity.getT1();
Integer t2 = genericity.getT2();
int result = t1 + t2;
System.out.println(result);
}
在創(chuàng)建泛型類對(duì)象時(shí),同樣在類名后添加,此時(shí)Integer就作為了T符合的內(nèi)容,所以類中的屬性都將是Integer類型。如果類名后添加,則類中的屬性均為String類型。
實(shí)際上,泛型類可以定義多個(gè)泛型變量,比如:
@Data
@AllArgsConstructor
@NoArgsConstructor
public class GenericityU> {
private T t1;
private U t2;
}
此時(shí)我們?cè)趧?chuàng)建對(duì)象時(shí)就可以傳入兩個(gè)類型:
public static void main(String[] args) {
GenericityString> genericity = new Genericity<>(1, "1");
Integer t1 = genericity.getT1();
String t2 = genericity.getT2();
} 03
定義泛型方法
泛型除了作用在類上,還可以作用在方法上,效果與泛型類是相似的。
public static void method(T t) {
}
只需在調(diào)用方法時(shí)傳入類型即可:
public static void main(String[] args) {
Genericity.<Integer>method(1);
}
而事實(shí)上,你無(wú)需這么寫,因?yàn)榫幾g器能夠自動(dòng)推斷出泛型類型T,所以調(diào)用泛型方法可以簡(jiǎn)寫為:
public static void main(String[] args) {
Genericity.method(1);
}
泛型方法的返回值也可以使用類型變量:
public static T method(T t) {
return t;
}
當(dāng)然了,泛型方法也支持定義多個(gè)類型變量:
public static T method(T t,U u) {
return t;
} 04
泛型的限定符
來(lái)看下面的程序:
public static T min(T[] t) {
T minimum = t[0];
for (int i = 0; i < t.length; i++) {
if (minimum.compareTo(t[i]) > 0) {
minimum = t[i];
}
}
return minimum;
}
能看得出來(lái)這段程序在干什么嗎?是的,它能夠取出數(shù)組t中的最小值,然而這段程序是有問(wèn)題的,因?yàn)門可以是任意類型的對(duì)象,但不是什么對(duì)象都能夠調(diào)用compareTo方法進(jìn)行比較的,所以,我們需要對(duì)類型變量T進(jìn)行限定,限定為實(shí)現(xiàn)了Comparable接口的對(duì)象,如下:
public static T min(T[] t) {
T minimum = t[0];
for (int i = 0; i < t.length; i++) {
if (minimum.compareTo(t[i]) > 0) {
minimum = t[i];
}
}
return minimum;
}
一個(gè)泛型方法也可以對(duì)類型變量進(jìn)行多個(gè)限定:
public static T min(T[] t) {
......
} 05
泛型擦除
需要知道的是,泛型僅僅是在編譯期間起作用,目的是讓程序員在編譯期間就避免發(fā)生一些類型不對(duì)應(yīng)的問(wèn)題,而在運(yùn)行階段,泛型是根本不存在的,因?yàn)樘摂M機(jī)會(huì)對(duì)泛型進(jìn)行擦除。
我們可以通過(guò)反射進(jìn)行驗(yàn)證,因?yàn)榉瓷涫亲饔迷谶\(yùn)行階段的:
public static void main(String[] args) throws Exception {
List list = new ArrayList<>();
list.add(1);
Method addMethod = list.getClass().getDeclaredMethod("add",Object.class);
addMethod.invoke(list,"2");
addMethod.invoke(list,true);
addMethod.invoke(list,3.2f);
System.out.println(list);
}
若是通過(guò)反射能夠?qū)⑦@些非Integer類型的值存入list,則說(shuō)明在運(yùn)行期間確實(shí)是不存在泛型檢查的,運(yùn)行結(jié)果如下:
[1, 2, true, 3.2]
泛型擦除也體現(xiàn)在泛型方法中,回顧之前的例子:
public static T min(T[] t) {
......
}
當(dāng)程序運(yùn)行期間,泛型會(huì)被擦除,此時(shí)方法變?yōu)槿缦拢?/p>public static Comparable min(Comparable t) {
......
}
這也就是為什么類型限定能夠生效的原因了,通過(guò)泛型擦除后,該方法就只能接收和返回Comparable接口的實(shí)現(xiàn)類。
06
泛型通配符
固定的泛型類型顯然無(wú)法滿足復(fù)雜多變的需求,為此,泛型設(shè)計(jì)者們還提供了泛型通配符,如:
Genericity extends Person>
它表示類型變量必須是Person類型的子類。當(dāng)然了,泛型通配符還有超類型定義的情況:
Genericity super Person>
此時(shí)類型變量就必須是Person類的超類。
還有一種情況是無(wú)限定通配符:
Genericity>
它和Genericity非常相似,但又有不同,Genericity>的setter方法不能被調(diào)用,getter方法只能返回Object類型,不過(guò)這種方式的用法較少,可能會(huì)被用來(lái)判斷空引用:
public static boolean isNull(Genericity> genericity){
return genericity.getContent();
}本文作者:汪偉俊?為Java技術(shù)迷專欄作者?投稿,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載
往 期 推 薦
1、致歉!抖音Semi Design承認(rèn)參考阿里Ant Design
2、對(duì)比7種分布式事務(wù)方案,還是偏愛(ài)阿里開(kāi)源的Seata,真香!
點(diǎn)分享
點(diǎn)收藏
點(diǎn)點(diǎn)贊
點(diǎn)在看





