Java這個(gè)高級(jí)特性,很多人還沒(méi)用過(guò)!
點(diǎn)擊“開(kāi)發(fā)者技術(shù)前線(xiàn)”,選擇“星標(biāo)??”
讓一部分開(kāi)發(fā)者看到未來(lái) 來(lái)源 | https://zhenbianshu.github.io/
前言
泛型是什么
generic,中文意思是通用的、一類(lèi)的,結(jié)合其應(yīng)用場(chǎng)景,我理解泛型是一種 通用類(lèi)型。但我們一般指泛型都是指其實(shí)現(xiàn)方式,也就是 將類(lèi)型參數(shù)化public static void quickSort(int[] data, int start, int end) {
int key = data[start];
int i = start;
int j = end;
while (i < j) {
while (data[j] > key && j > i) {
j--;
}
data[i] = data[j];
while (data[i] < key && i < j) {
i++;
}
data[j] = data[i];
}
data[i] = key;
if (i - 1 > start) {
quickSort(data, start, i - 1);
}
if (i + 1 < end) {
quickSort(data, i + 1, end);
}
}
public static <T extends Comparable<T>> void quickSort(T[] data, int start, int end) {
T key = data[start];
int i = start;
int j = end;
while (i < j) {
while (data[j].compareTo(key) > 0 && j > i) {
j--;
}
data[i] = data[j];
while (data[i].compareTo(key) < 0 && i < j) {
i++;
}
data[j] = data[i];
}
data[i] = key;
if (i - 1 > start) {
quickSort(data, start, i - 1);
}
if (i + 1 < end) {
quickSort(data, i + 1, end);
}
}
當(dāng)參數(shù)類(lèi)型不明確,可能會(huì)擴(kuò)展為多種時(shí)。 想聲明參數(shù)類(lèi)型為 Object,并在使用時(shí)用instanceof判斷時(shí)。
泛型只能替代Object的子類(lèi)型,如果需要替代基本類(lèi)型,可以使用包裝類(lèi),至于為什么,會(huì)在下文中說(shuō)明。使用
聲明
<占位符 [,另一個(gè)占位符] > 的形式,需要在一個(gè)地方同時(shí)聲明多個(gè)占位符時(shí),使用 , 隔開(kāi)。占位符的格式并無(wú)限制,不過(guò)一般約定使用單個(gè)大寫(xiě)字母,如 T 代表類(lèi)型(type),E 代表元素*(element)等。雖然沒(méi)有嚴(yán)格規(guī)定,不過(guò)為了代碼的易讀性,最好使用前檢查一下約定用法。class Generics<T> { // 在類(lèi)名后聲明引入泛型類(lèi)型
private T field; // 引入后可以將字段聲明為泛型類(lèi)型
public T getField() { // 類(lèi)方法內(nèi)也可以使用泛型類(lèi)型
return field;
}
}
public [static] <T> void testMethod(T arg) { // 訪(fǎng)問(wèn)限定符[靜態(tài)方法在 static] 后使用 <占位符> 聲明泛型方法后,在參數(shù)列表后就可以使用泛型類(lèi)型了
// doSomething
}
Comparable<T> 的泛型接口,與此類(lèi)似的還有 Searializable<T> Iterable<T>等,其實(shí)在接口中聲明與在類(lèi)中聲明并沒(méi)有什么太大區(qū)別。調(diào)用
public static void main(String[] args) {
String[] strArr = new String[2];
// 泛型方法的調(diào)用跟普通方法相同
Generics.quickSort(strArr, 0, 30 );
// 泛型類(lèi)在調(diào)用時(shí)需要聲明一種精確類(lèi)型
Generics<Long> sample = new Generics<>();
Long field = sample.getField();
}
// 泛型接口需要在泛型類(lèi)里實(shí)現(xiàn)
class GenericsImpl<T> implements Comparable<T> {
@Override
public int compareTo(T o) {
return 0;
}
}
類(lèi)型擦除
由來(lái)
Generics<Long> 被擦除后是 Generics,我們常用的 List<String> 被擦除后只剩下 List。public static void main(String[] args) {
List<String> stringList = new ArrayList<>();
List<Long> longList = new ArrayList<>();
if (stringList.getClass() == longList.getClass()) {
System.out.println(stringList.getClass().toString());
System.out.println(longList.getClass().toString());
System.out.println("type erased");
}
}
class java.util.ArrayList,兩者類(lèi)型相同,說(shuō)明其泛型類(lèi)型被擦除掉了。signature 字段,其中指向了常量表中泛型的真正類(lèi)型,所以泛型的真正類(lèi)型,還可以通過(guò)反射獲取得到。實(shí)現(xiàn)
javac 命令編譯成 class 文件后,再使用 javap 命令查看其字節(jié)碼信息:
T 被替換成了 Object 類(lèi)型,而在 main 方法里 getField 字段時(shí),進(jìn)行了類(lèi)型轉(zhuǎn)換(checkcast),如此,我們可以看出來(lái) Java 的泛型實(shí)現(xiàn)了,一段泛型代碼的編譯運(yùn)行過(guò)程如下:編譯期間編譯器檢查傳入的泛型類(lèi)型與聲明的泛型類(lèi)型是否匹配,不匹配則報(bào)出編譯器錯(cuò)誤; 編譯器執(zhí)行類(lèi)型擦除,字節(jié)碼內(nèi)只保留其原始類(lèi)型; 運(yùn)行期間,再將 Object 轉(zhuǎn)換為所需要的泛型類(lèi)型。
Java 的泛型實(shí)際上是由編譯器實(shí)現(xiàn)的,將泛型類(lèi)型轉(zhuǎn)換為 Object 類(lèi)型,在運(yùn)行期間再進(jìn)行狀態(tài)轉(zhuǎn)換。實(shí)踐問(wèn)題
具體類(lèi)型須為Object子類(lèi)型
Generics<int> generics = new Generics<int>(); 在編譯期間就會(huì)報(bào)錯(cuò)的。邊界限定通配符的使用
<? extends Generics> 是上邊界限定通配符,避開(kāi) 上邊界 這個(gè)比較模糊的詞不談,我們來(lái)看其聲明 xx extends Generics, XX 是繼承了 Generics 的類(lèi)(也有可能是實(shí)現(xiàn),下面只說(shuō)繼承),我們按照以下代碼聲明:List<? extends Generics> genericsList = new ArrayList<>();
Generics generics = genericsList.get(0);
genericsList.add(new Generics<String>()); // 編譯無(wú)法通過(guò)
<? super Generics> 是下邊界限定通配符, XX 是 Generics 的父類(lèi),所以:List<? super Generics> genericsList = new ArrayList<>();
genericsList.add(new Generics()); // 編譯無(wú)法通過(guò)
Generics generics = genericsList.get(0);
最佳實(shí)踐
將代碼邏輯拆分為兩部分:通用邏輯和類(lèi)型相關(guān)邏輯;通用邏輯是一些跟參數(shù)類(lèi)型無(wú)關(guān)的邏輯,如快排的元素位置整理等;類(lèi)型相關(guān)邏輯,顧名思義,是需要確定類(lèi)型后才能編寫(xiě)的邏輯,如元素大小的比較,String 類(lèi)型的比較和 int 類(lèi)型的比較就不一樣。 如果沒(méi)有類(lèi)型相關(guān)的邏輯,如 List 作為容器不需要考慮什么類(lèi)型,那么直接完善通用代碼即可。 如果有參數(shù)類(lèi)型相關(guān)的邏輯,那么就需要考慮這些邏輯是否已有共同的接口實(shí)現(xiàn),如果已有共同的接口實(shí)現(xiàn),可以使用邊界限定通配符。如快排的元素就實(shí)現(xiàn)了 Compare接口,Object 已經(jīng)實(shí)現(xiàn)了toString()方法,所有的打印語(yǔ)句都可以調(diào)用它。如果還沒(méi)有共同的接口,那么需要考慮是否可以抽象出一個(gè)通用的接口實(shí)現(xiàn),如打印人類(lèi)的衣服顏色和動(dòng)物的毛皮顏色,就可以抽象出一個(gè) getColor()接口,抽象之后再使用邊界限定通配符。如果無(wú)法抽象出通用接口,如輸出人類(lèi)身高或動(dòng)物體重這種,還是不要使用泛型了,因?yàn)椴幌薅?lèi)型的話(huà),具體類(lèi)型的方法調(diào)用也就無(wú)從談起,編譯也無(wú)法通過(guò)。

小結(jié)
— 完 —
點(diǎn)這里??關(guān)注我,記得標(biāo)星呀~
前線(xiàn)推出學(xué)習(xí)交流一定要備注:研究/工作方向+地點(diǎn)+學(xué)校/公司+昵稱(chēng)(如JAVA+上海+上交+可可),根據(jù)格式備注,可更快被通過(guò)且邀請(qǐng)進(jìn)群
掃碼加小編微信,進(jìn)群和大佬們零距離
END 后臺(tái)回復(fù)“電子書(shū)” “資料” 領(lǐng)取一份干貨,數(shù)百面試手冊(cè)等你 開(kāi)發(fā)者技術(shù)前線(xiàn) ,匯集技術(shù)前線(xiàn)快訊和關(guān)注行業(yè)趨勢(shì),大廠干貨,是開(kāi)發(fā)者經(jīng)歷和成長(zhǎng)的優(yōu)秀指南。 歷史推薦
騰訊 Code Review 規(guī)范出爐! 一場(chǎng)由MyBatis版本升級(jí)引發(fā)的線(xiàn)上慘案 Spring 官宣,拋棄原生 JVM! 我見(jiàn)過(guò)最“騷”的代碼注釋?zhuān)∩瘾F版都來(lái)了 好文點(diǎn)個(gè)在看吧!
評(píng)論
圖片
表情

