日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

一句话,讲清楚java泛型的本质(非类型擦除)

發布時間:2025/7/25 编程问答 26 豆豆
生活随笔 收集整理的這篇文章主要介紹了 一句话,讲清楚java泛型的本质(非类型擦除) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

?歡迎關注我的公眾號“彤哥讀源碼”,查看更多源碼系列文章, 與彤哥一起暢游源碼的海洋。


背景

昨天,在逛論壇時遇到個這么個問題,上代碼:

public class GenericTest {//方法一public static <T extends Comparable<T>> List<T> sort(List<T> list) {return Arrays.asList(list.toArray((T[]) new Comparable[list.size()]));}//方法二public static <T extends Comparable<T>> T[] sort2(List<T> list) {// 這里沒報錯return list.toArray((T[]) new Comparable[list.size()]);}public static void main(String[] args) {List<Integer> list = new ArrayList<>();list.add(1);list.add(2);// 方法一調用正常System.out.println(sort(list).getClass());// 方法二調用報錯了,這里報錯了System.out.println(sort2(list).getClass());} } 復制代碼

這個問題有以下四個現象:

(1)方法一調用完全正常;

(2)方法二調用報錯了;

(3)方法二報錯的地方是在System.out.println(sort2(list).getClass());這行,而不是return list.toArray((T[]) new Comparable[list.size()]);這行;

(4)報的錯是[Ljava.lang.Comparable; cannot be cast to [Ljava.lang.Integer;;

怎么樣?你心中有答案嘛?類型擦除?怎么擦?摩擦摩擦?

解決

剛拿到這道題,我也是一臉懵逼,這要報錯也應該是在return list.toArray((T[]) new Comparable[list.size()]);這行啊,而且要報錯應該兩個方法都報錯啊。

抱著不放棄不拋棄的心態,彤哥做了大量的實驗,終于得出了泛型的本質,且聽我娓娓道來。

小插曲

首先,我們要明白,java中的數組是不支持向下轉型的,但是如果本身就是那個類型的是可以轉過去的,請看下面的例子:

public static void main(String[] args) {Object[] objs = new Object[]{1};// 類型轉換錯誤 // Integer[] ins = (Integer[]) objs;Object[] objs2 = new Integer[]{1};// 不報錯Integer[] ins2 = (Integer[]) objs2;}復制代碼

類型擦除

java里的泛型是假泛型,只在編譯期有效,在運行時是沒有泛型的概念的,舉個簡單的例子:

public static void main(String[] args) {List<String> strList = Arrays.asList("1");List<Integer> intList = Arrays.asList(1);// 打印:trueSystem.out.println(strList.getClass() == intList.getClass());} 復制代碼

可以看到兩個list的類型是一樣的,如果你覺得這個例子不夠說服力,那我給你個過分點的例子:

public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {List<String> strList = new ArrayList<>();Method addMethod = strList.getClass().getMethod("add", Object.class);addMethod.invoke(strList, 1);addMethod.invoke(strList, true);addMethod.invoke(strList, new Long(1));addMethod.invoke(strList, new Byte[]{1});// 打印:[1, true, 1, 1]System.out.println(strList); } 復制代碼

瞧,我可以往一個String類型的List中扔任何我想扔的東西,服不服?!

所以說java里面的泛型是假的,運行時不存在滴。

回歸正題

數組不能向下強轉我懂了,類型擦除我也懂了,似乎還是過不好這一生,呃不是,是還是解決不了這道題啊?

呃,好像是~~

我們再來看一個簡單的例子:

// GenericTest2.java(源碼) public class GenericTest2 {public static void main(String[] args) {System.out.println(raw("1"));}public static <T> T raw(T t) {return t;} } // GenericTest2.class(反編譯) public class GenericTest2 {public GenericTest2() {}public static void main(String[] args) {System.out.println((String)raw("1"));}public static <T> T raw(T t) {return t;} } 復制代碼

嗯~似乎看出來點端倪,反編譯后多了個構造方法。

呃,沒錯。還有呢?

仔細一看,System.out.println((String)raw("1"));這一句多加了個String強轉。

這就是關鍵所在,結合類型擦除,運行時并沒有所謂的泛型,所以raw()返回的其實是Object,但是調用者自己知道我要的是String類型啊,所以我就知道強轉一下嘍。

我們再來看個極端的例子:

// GenericTest2.java(源碼) public class GenericTest2 {public static void main(String[] args) {System.out.println(raw("1"));}public static <T> T raw(T t) {return (T)new Integer(1);} } // GenericTest2.class(反編譯) public class GenericTest2 {public GenericTest2() {}public static void main(String[] args) {System.out.println((String)raw("1"));}public static <T> T raw(T t) {return new Integer(1);} } 復制代碼

仔細觀察,可以發現,raw()方法里的強轉(T)new Integer(1)變成了new Integer(1),強轉被擦除了,實際上在運行時這里的T變成了Object,所有類型都是Object的子類,也就不需要強轉了。

而(String)raw("1")的強轉還是加上的,這是調用者知道類型是String,所以raw()返回后自己強轉成String一下。

當然,這個代碼運行是會報錯的,java.lang.Integer cannot be cast to java.lang.String,因為raw()返回的是Integer類型,強轉成String類型失敗了。

好了,基本思路就是這樣。

泛型類呢?

我們上面舉的例子都是泛型方法,那么泛型類呢?

同樣地,我們來看個例子:

// GenericTest3.java(源碼) public class GenericTest3 {public static void main(String[] args) {System.out.println(new Raw<String>().raw("1"));} } class Raw<T> {public T raw(T t) {return (T)new Integer(1);} } // GenericTest3.class(反編譯) public class GenericTest3 {public GenericTest3() {}public static void main(String[] args) {System.out.println((String)(new Raw()).raw("1"));} } class Raw<T> {Raw() {}public T raw(T t) {return new Integer(1);} } 復制代碼

可以看到,跟泛型方法的表現一模一樣。當然,這里運行時也會報java.lang.Integer cannot be cast to java.lang.String這個錯誤。

總結

java中的泛型只在編譯期有效,在運行時只有調用者知道需要什么類型,且調用者調用泛型方法后自己做強制轉換,被調用者是完全無感的。

所以,出現問題不要問被調用者,而是要問調用者,你丫是怎么調用的?!

解答開篇

為了方便我們還是把開篇的問題拿過來。

// GenericTest.java(源碼) public class GenericTest {//方法一public static <T extends Comparable<T>> List<T> sort(List<T> list) {return Arrays.asList(list.toArray((T[]) new Comparable[list.size()]));}//方法二public static <T extends Comparable<T>> T[] sort2(List<T> list) {// 這里沒報錯return list.toArray((T[]) new Comparable[list.size()]);}public static void main(String[] args) {List<Integer> list = new ArrayList<>();list.add(1);list.add(2);// 方法一調用正常System.out.println(sort(list).getClass());// 方法二調用報錯了,這里報錯了System.out.println(sort2(list).getClass());} } 復制代碼

這里似乎又不太一樣,變成了<T extends Comparable<T>>,其實是一樣的啦,如果單獨寫<T>是相當于<T extends Object>的。

那么,我們就延伸一下,被調用者是完全無感的,它只能盡力拿到它知道的類型,比如這里就只能盡力拿到Comparable,如果是<T>拿到的就是Object。

所以,方法二返回的就是實打實的Comparable[]類型,作為被調用者,它一點問題都沒有。

但是,調用方是知道我需要的是Integer[]類型的,因為list里面是Integer類型,所以返回的應該是Integer[]類型,所以我就強轉嘍,然后就報錯了。

到底是不是這樣?我們來看看反編譯后的代碼:

// GenericTest.class(反編譯) public class GenericTest {public GenericTest() {}public static <T extends Comparable<T>> List<T> sort(List<T> list) {return Arrays.asList(list.toArray((Comparable[])(new Comparable[list.size()])));}public static <T extends Comparable<T>> T[] sort2(List<T> list) {// 這里使用的是Comparable[]強轉,所以返回的也是實打實的Comparable[]類型return (Comparable[])list.toArray((Comparable[])(new Comparable[list.size()]));}public static void main(String[] args) {List<Integer> list = new ArrayList();list.add(1);list.add(2);System.out.println(sort(list).getClass());// 數組向下轉型失敗System.out.println(((Integer[])sort2(list)).getClass());} } 復制代碼

可以看到,跟我們的分析完全一致。

一句話,一輩子

java中的泛型只在編譯期有效,在運行時只有調用者知道它自己需要什么類型,且調用者調用泛型方法后自己做強制轉換,被調用者是完全無感的,被調用者只能盡力拿到它所知道的類型。

此時,我的腦海中不經響起那熟悉的旋律,“一句話,一輩子……”,今天的這句話你記住了嗎?


歡迎關注我的公眾號“彤哥讀源碼”,查看更多源碼系列文章, 與彤哥一起暢游源碼的海洋。

轉載于:https://juejin.im/post/5cb16ef06fb9a068ac3df0a3

總結

以上是生活随笔為你收集整理的一句话,讲清楚java泛型的本质(非类型擦除)的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。

主站蜘蛛池模板: 精品少妇一二三区 | 自拍日韩亚洲一区在线 | 黑人巨大猛交丰满少妇 | 欧美一区二区在线观看视频 | 国产成人啪精品 | 日韩综合精品 | 男女操操操 | www.日本在线 | 中文字幕 日本 | 色婷婷国产精品久久包臀 | 日韩在线一 | 香蕉国产| 国产精品久久久久永久免费看 | 先锋av资源网 | 麻豆视频网址 | 国产视频成人 | 激情小说欧美色图 | 久久久一级 | 国产精品第3页 | 草逼视频网站 | 午夜之声l性8电台lx8电台 | 日本在线看 | 四虎影库在线播放 | 久久久精品网 | 艳母免费在线观看 | 色综合福利 | 免费黄色一区二区 | 国产美女性生活 | 91福利视频免费观看 | jzzijzzij亚洲成熟少妇18 欧美www在线观看 | 五月天婷婷激情视频 | 国产中文在线观看 | 偷自在线 | 免费成人深夜夜行网站视频 | 亚洲欧美另类视频 | 亚洲视频免费在线 | 超碰狠狠干 | 美女扒开腿让男生桶 | 久久久影视 | 欧美黄色录像视频 | 亚洲欧美v| 久久99热这里只频精品6学生 | 欧美激情视频一区二区三区在线播放 | 片黄在线观看 | 亚欧洲精品在线视频免费观看 | 国产特黄大片aaaa毛片 | 亚洲福利| 国产不卡二区 | 日本大尺度做爰呻吟舌吻 | 一本大道久久久久精品嫩草 | 久久久91| 少妇粉嫩小泬白浆流出 | 日韩一级免费片 | 东京热av一区 | 露脸丨91丨九色露脸 | 日韩视频一区二区三区四区 | 殴美一级视频 | 都市激情麻豆 | 精久久久久 | 91视频在线观看免费 | 永久免费在线 | 亚洲精品国产精品乱码桃花 | 污污视频在线观看网站 | 日韩欧美亚洲精品 | 国产鲁鲁 | 巨乳美女在线 | 歪歪6080 | 欧美色图小说 | 中国av免费看 | 亚洲精品久久久久久国 | 一出一进一爽一粗一大视频 | 欧美日韩123区 | 国产精品日韩一区二区三区 | 色哟哟网站在线观看 | 久久av导航 | 神马午夜在线观看 | 国产黄色片免费看 | 亚洲成人a∨| 精品无码av一区二区三区不卡 | 午夜老司机福利 | 超碰97在线免费 | 国产美女www | 亚洲wwwwww | 色婷婷色综合 | 久久香蕉精品视频 | 天天干天天操心 | 亚洲av成人精品日韩在线播放 | 国产美女作爱视频 | 久草成人在线视频 | 亚洲欧美小视频 | 五月天婷婷激情 | 亚洲av永久无码精品一区二区国产 | 亚洲国产91 | 毛片在线免费观看网址 | 日韩精品在线观看网站 | 久久伊人网站 | 亚洲性视频 | 孕妇丨91丨九色 | av作品在线观看 |