国产精品电影_久久视频免费_欧美日韩国产激情_成年人视频免费在线播放_日本久久亚洲电影_久久都是精品_66av99_九色精品美女在线_蜜臀a∨国产成人精品_冲田杏梨av在线_欧美精品在线一区二区三区_麻豆mv在线看

用了這么多年的 Java 泛型,你對它到底有多了解?

新聞 前端
作為一個 Java 程序員,日常編程早就離不開泛型。泛型自從 JDK1.5 引進(jìn)之后,真的非常提高生產(chǎn)力。

 [[327224]]

作為一個 Java 程序員,日常編程早就離不開泛型。泛型自從 JDK1.5 引進(jìn)之后,真的非常提高生產(chǎn)力。一個簡單的泛型 T ,寥寥幾行代碼, 就可以讓我們在使用過程中動態(tài)替換成任何想要的類型,再也不用實(shí)現(xiàn)繁瑣的類型轉(zhuǎn)換方法。

雖然我們每天都在用,但是還有很多同學(xué)可能并不了解其中的實(shí)現(xiàn)原理。今天這篇我們從以下幾點(diǎn)聊聊 Java 泛型:

  • Java 泛型實(shí)現(xiàn)方式

  • 類型擦除帶來的缺陷

  • Java 泛型發(fā)展史

 

 

 

 

Java 泛型實(shí)現(xiàn)方式

Java 采用**類型擦除(Type erasure generics)**的方式實(shí)現(xiàn)泛型。用大白話講就是這個泛型只存在源碼中,編譯器將源碼編譯成字節(jié)碼之時,就會把泛型『 擦除 』,所以字節(jié)碼中并不存在泛型。

對于下面這段代碼,編譯之后,我們使用 javap -s class 查看字節(jié)碼。

 

 

方法源碼
字節(jié)碼

 

 

觀察 setParam 部分的字節(jié)碼,從 descriptor 可以看到,泛型 T 已被擦除,最終替換成了 Object 。

ps:并不是每一個泛型參數(shù)被擦除類型后都會變成 Object 類,如果泛型類型為  T extends String 這種方式,最終泛型擦除之后將會變成 String。

同理 getParam 方法,泛型返回值也被替換成了 Object 。

為了保證 String param = genericType.getParam(); 代碼的正確性,編譯器還得在這里插入類型轉(zhuǎn)換。

除此之外,編譯器還會對泛型安全性防御,如果我們往 ArrayList<String> 添加 Integer ,程序編譯期間就會報錯。

最終類型擦除后的代碼等同與如下:

 

 

 

 

類型擦除帶來的缺陷

作為對比,我們再來簡單聊下 C# 泛型的實(shí)現(xiàn)方式。

**C#**泛型實(shí)現(xiàn)方式為「 具現(xiàn)化式泛型(Reifiable generics) 」,不熟悉的 C# 小伙伴可以不用糾結(jié) 具現(xiàn)化 技術(shù)概念,我也不了解這些特性--!

簡單點(diǎn)來講,**C#**實(shí)現(xiàn)的泛型,無論是在程序源碼,還是在編譯之后的,甚至是運(yùn)行期間都是切實(shí)存在的。

相對比與 C# 泛型,Java 泛型看起來就像是個「  」泛型。Java 泛型只存在程序源碼中,編譯之后就被擦除,這種缺陷相應(yīng)的會帶來一些問題。

不支持基本數(shù)據(jù)類型

泛型參數(shù)被擦除之后,強(qiáng)制變成了 Object 類型。這么做對于引用類型來說沒有什么問題,畢竟 Object 是所有類型的父類型。但是對于 int/long 等八個基本數(shù)據(jù)類型說,這就難辦了。因?yàn)?Java 沒辦法做到 int/long 與 Object 的強(qiáng)制轉(zhuǎn)換。

如果要實(shí)現(xiàn)這種轉(zhuǎn)換,需要進(jìn)行一系列改造,改動難度還不小。所以當(dāng)時 Java 給出一個簡單粗暴的解決方案:既然沒辦法做到轉(zhuǎn)換,那就索性不支持原始類型泛型了。

如果需要使用,那就規(guī)定使用相關(guān)包裝類的泛型,比如 ArrayList<Integer> 。另外為了開發(fā)人員方便,順便增加了原生數(shù)據(jù)類型的 自動拆箱/裝箱 的特性。

正是這種「偷懶」的做法,導(dǎo)致現(xiàn)在我們沒辦法使用原始類型泛型,又要忍受包裝類裝箱/拆箱帶來的開銷,從而又帶來運(yùn)行效率的問題。

運(yùn)行效率

上面字節(jié)碼例子我們已經(jīng)看到,泛型擦除之后類型將會變成 Object 。當(dāng)泛型出現(xiàn)在方法輸入位置的時候,由于 Java 是可以向上轉(zhuǎn)型的,這里并不需要強(qiáng)制類型轉(zhuǎn)換,所以沒有什么問題。

但是當(dāng)泛型參數(shù)出現(xiàn)在方法的輸出位置(返回值)的時候,調(diào)用該方法的地方就需要進(jìn)行向下轉(zhuǎn)換,將 Object 強(qiáng)制轉(zhuǎn)換成所需類型,所以編譯器會插入一句 checkcast 字節(jié)碼。

除了這個,上面我們還說到原始基本數(shù)據(jù)類型,編譯器還需幫助我們進(jìn)行裝箱/拆箱。

所以對于下面這段代碼來說:

  1. List<Integer> list = new ArrayList<Integer>(); 
  2. list.add(66); // 1 
  3. int num = list.get(0); // 2 

對于①處,編譯器要做就是增加基本類型的裝箱即可。但是對于第二步來說,編譯器首先需要將 Object 強(qiáng)制轉(zhuǎn)換成 Integer ,接著編譯器還需要進(jìn)行拆箱。

類型擦除之后,上面代碼等同于:

  1. List list = new ArrayList(); 
  2. list.add(Integer.valueOf(66)); 
  3. int num = ((Integer) list.get(0)).intValue(); 

如果上面泛型代碼在 C# 實(shí)現(xiàn),就不會有這么多額外步驟。所以 Java 這種類型擦除式泛型實(shí)現(xiàn)方式無論使用效果與運(yùn)行效率,還是全面落后于 C# 的具現(xiàn)化式泛型。

運(yùn)行期間無法獲取泛型實(shí)際類型

由于編譯之后,泛型就被擦除,所以在代碼運(yùn)行期間,Java 虛擬機(jī)無法獲取泛型的實(shí)際類型。

下面這段代碼,從源碼上兩個 List 看起來是不同類型的集合,但是經(jīng)過泛型擦除之后,集合都變?yōu)?nbsp;ArrayList 。所以 if 語句中代碼將會被執(zhí)行。

  1. ArrayList<Integer> li = new ArrayList<Integer>(); 
  2. ArrayList<Float> lf = new ArrayList<Float>(); 
  3. if (li.getClass() == lf.getClass()) { // 泛型擦除,兩個 List 類型是一樣的 
  4.     System.out.println("6666"); 

這樣代碼看起來就有點(diǎn)反直覺,這對新手來說不是很友好。

另外還會給我們在實(shí)際使用中帶來一些限制,比如說我們沒辦法直接實(shí)現(xiàn)以下代碼:

 

 

 

 

最后再舉個例子,比如說我們需要實(shí)現(xiàn)一個泛型 List 轉(zhuǎn)換成數(shù)組的方法,我們就沒辦法直接從 List 去獲取泛型實(shí)際類型,所以我們不得不額外再傳入一個 Class 類型,指定數(shù)組的類型:

  1. public static <E> E[] convert(List<E> list, Class<E> componentType) { 
  2.     E[] array = (E[]) Array.newInstance(componentType, list.size()); 
  3.     .... 

從上面的例子我們可以看到,Java 采用類型擦除式實(shí)現(xiàn)泛型,缺陷很多。那為什么 Java 不采用 C# 的那種泛型實(shí)現(xiàn)方式?或者說采用一種更好實(shí)現(xiàn)方式?

這個問題等我們了解 Java 泛型機(jī)制的歷史,以及當(dāng)時 Java 語言的現(xiàn)狀,我們才能切身體會到當(dāng)時 Java 采用這種泛型實(shí)現(xiàn)方式的原因。

Java 泛型歷史背景

Java 泛型最早是在 JDK5 的時候才被引入,但是泛型思想最早來自來自 C++ 模板(template)。1996 年 Martin Odersky(Scala 語言締造者) 在剛發(fā)布的 Java 的基礎(chǔ)上擴(kuò)展了泛型、函數(shù)式編程等功能,形成一門新的語言-「 Pizza 」。

后來,Java 核心開發(fā)團(tuán)隊對 Pizza 的泛型設(shè)計深感興趣,與 Martin 合作,一起合作開發(fā)的一個新的項目「 Generic Java 」。這個項目的目的是為了給 Java 增加泛型支持,但是不引入函數(shù)式編程等功能。最終成功在 Java5 中正式引入泛型支持。

 

 

 

 

泛型移植過程,一開始并不是朝著類型擦除的方向前進(jìn),事實(shí) Pizza 中泛型更加類似于 C# 中的泛型。

但是由于 Java 自身特性,自帶嚴(yán)格的約束,讓 Martin 在 Generic Java 開發(fā)過程中,不得不放棄了 Pizza 中泛型設(shè)計。

這個特性就是,Java 需要做到 嚴(yán)格的向后兼容性 。也就是說一個在 JDK1.2 編譯出來 Class 文件,不僅能在 JDK 1.2 能正常運(yùn)行,還得必須保證在后續(xù) JDK,比如 JDK12 中也能保證正常的運(yùn)行。

這種特性是明確寫入 Java 語言規(guī)范的,這是一個對 Java 使用者的一個嚴(yán)肅承諾。

這里強(qiáng)調(diào)一下,這里的向后兼容性指的是二進(jìn)制兼容性,并不是源碼兼容性。也不保證高版本的 Class 文件能夠運(yùn)行在低版本的 JDK 上。

現(xiàn)在困難點(diǎn)在于,Java 1.4.2 之前都沒有支持泛型,而 Java5 之后突然要支持泛型,還要讓 JDK1.4 之前編譯的程序能在新版本中正常運(yùn)行,這就意味著以前沒有的限制,就不能突然增加。

舉個例子:

  1. ArrayList arrayList=new ArrayList(); 
  2. arrayList.add("6666"); 
  3. arrayList.add(Integer.valueOf(666)); 

沒有泛型之前, List 集合是可以存儲不同類型的數(shù)據(jù),那么引入泛型之后,這段代碼必須的能正確運(yùn)行。

為了保證這些舊的 Clas 文件能在 Java5 之后正常運(yùn)行,設(shè)計者基本有兩條路:

  1. 需要泛型化的容器(主要是容器類型),以前有的保持不變,平行增加一套新的泛型化的版本。

  2. 直接把已有的類型原地泛型化,不增加任何新的已有類型的泛型版本。

如果 Java 采用第一條路實(shí)現(xiàn)方式,那么現(xiàn)在我們可能就會有兩套集合類型。以 ArrayList 為例,一套為普通的 java.util.ArrayList ,一套可能為 java.util.generic.ArrayList<T> 。

采用這種方案之后,如果開發(fā)中需要使用泛型特性,那么直接使用新的類型。另外舊的代碼不改動,也可以直接運(yùn)行在新版本 JDK 中。

這套方案看起來沒什么問題,實(shí)際上C# 就是采用這套方案。但是為什么 Java  卻沒有使用這套方案那?

這是因?yàn)楫?dāng)時 C# 才發(fā)布兩年,歷史代碼并不多,如果舊代碼需要使用泛型特性,改造起來也很快。但是 Java 不一樣,當(dāng)時 Java 已經(jīng)發(fā)布十年了,已經(jīng)有很多程序已經(jīng)運(yùn)行部署在生產(chǎn)環(huán)境,可以想象歷史代碼非常多。

如果這些應(yīng)用在新版本 Java 需要使用泛型,那就需要做大量源碼改動,可以想象這個開發(fā)工作量。

另外 Java 5   之前,其實(shí)我們就已經(jīng)有了兩套集合容器,一套為 Vector/Hashtable 等容器,一套為 ArrayList/ HashMap 。這兩套容器的存在,其實(shí)已經(jīng)引來一些不便,對于新接觸的 Java 的開發(fā)人員來說,還得學(xué)習(xí)這兩者的區(qū)別。

如果此時為了泛型再引入新類型,那么就會有四套容器同時并存。想想這個畫面,一個新接觸開發(fā)人員,面對四套容器,完全不知道如何下手選擇。如何 Java 真的這么實(shí)現(xiàn)了,想必會有更多人吐槽 Java。

所以 Java 選擇第二條路,采用類型擦除,只需要改動 Javac 編譯器,不需要改動字節(jié)碼,不需要改動虛擬機(jī),也保證了之前歷史沒有泛型的代碼還可以在新的 JDK 中運(yùn)行。

但是第二條路,并不代表一定需要使用類型擦除實(shí)現(xiàn),如果有足夠時間好好設(shè)計,也許會有更好的方案。

當(dāng)年留下的技術(shù)債,現(xiàn)在只能靠 Valhalla 項目來還了。這個項目從2014 年開始立項,原本計劃在 JDK10 中解決現(xiàn)有語言的各種缺陷。但是結(jié)果我們也知道了,現(xiàn)在都 JDK14 了,還只是完成少部分目標(biāo),并沒有解決核心目標(biāo),可見這個改動的難度啊。

總結(jié)

本文我們先從 Java 泛型底層實(shí)現(xiàn)方式開始聊起,接著舉了幾個例子,讓大家了解現(xiàn)在泛型實(shí)現(xiàn)方式存在一些缺陷。

然后我們帶入 Java 泛型歷史背景,站在 Java 核心開發(fā)者的角度,才能了解 Java 泛型這么現(xiàn)實(shí)無奈原因。

最后作為 Java 開發(fā)者,讓我們對于現(xiàn)在  Java 一些不足,少些抱怨,多一些理解吧。相信之后 Java 核心開發(fā)人員肯定會解決泛型現(xiàn)有的缺陷,讓我們拭目以待。

 

 

責(zé)任編輯:張燕妮 來源: 程序通事
相關(guān)推薦

2023-09-28 11:45:09

泛型類對象編譯器

2020-05-29 14:18:12

Java泛型數(shù)據(jù)

2023-11-13 08:49:54

2018-10-06 21:51:37

代碼SOLID編程

2018-10-07 06:30:40

代碼設(shè)計模式面向?qū)ο笤瓌t

2022-12-26 07:43:44

SpringBootWeb 類框架的

2013-07-09 09:55:33

科技年齡半導(dǎo)體

2015-03-27 10:20:41

谷歌地圖谷歌偉大

2021-10-07 23:24:21

手機(jī)關(guān)機(jī)重啟

2024-02-20 08:09:51

Java 8DateUtilsDate工具類

2018-08-28 16:10:36

2020-07-21 18:37:14

代碼條件變量

2019-12-06 14:50:49

APP錄音隱私

2021-04-16 11:15:22

蘋果手機(jī)屏幕

2021-09-11 22:56:58

微信功能技巧

2021-09-06 11:39:39

筆記本噪音風(fēng)扇

2021-09-03 00:31:17

iPhone手機(jī)截圖

2017-11-30 07:30:27

程序員代碼軟件世界觀

2021-09-21 16:18:07

手機(jī)電池快充

2019-11-27 10:54:43

Tomcat連接數(shù)線程池
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號

婷婷开心激情综合| 另类的小说在线视频另类成人小视频在线| 日韩免费高清在线| 亚洲香蕉成人av网站在线观看| 精品综合久久久久久8888| caoprom在线| 日日骚一区二区网站| 日韩欧美一级在线播放| 日本一不卡视频| h片在线观看视频免费| 99视频精品全部免费看| 在线中文字幕日韩| 91在线国产观看| 9l视频自拍蝌蚪9l视频成人| 国产色视频在线播放| 日韩av观看网址| 激情久久av一区av二区av三区| 自拍欧美日韩| 91在线中字| 在线视频一二三区| 久久激情视频免费观看| 亚洲日本欧美天堂| 国内精品美女在线观看| 在线黄色网页| 日韩精品 欧美| 51精品在线观看| 婷婷综合另类小说色区| 亚洲啪啪91| 日产福利视频在线观看| 久久久久免费精品| 国产精品丝袜一区二区三区| 欧洲精品在线观看| 全国精品久久少妇| 视频91a欧美| 116美女写真午夜一级久久| 国产精品午夜av在线| 亚洲精品国产品国语在线| 久久久久久影视| 成人免费在线观看av| 日本高清视频在线观看| 大桥未久一区二区| 久久久久久久久久久免费精品| 午夜久久福利影院| 三级在线观看一区二区 | 国产精品精品国产| 777欧美精品| 久久婷婷国产综合国色天香 | av在线电影网站| 日韩欧美精品久久| 欧美激情精品久久久久久免费印度| 婷婷综合久久一区二区三区| 日韩黄色小视频| 国产成人高清精品免费5388| h视频网站在线观看| 青青青在线观看视频| 国产精品视频导航| 亚洲欧美国产日韩天堂区| 亚洲美女精品一区| 美女mm1313爽爽久久久蜜臀| 日韩啪啪网站| 金瓶狂野欧美性猛交xxxx| 一区二区三区入口| 精品免费日产一区一区三区免费| 国产一区二区黑人欧美xxxx| 亚洲成人免费视频| 国精产品一区一区三区mba视频 | 日韩电影在线一区| 精品欠久久久中文字幕加勒比| 黄色片网站在线观看| av在线无限看| 日本成人看片网址| 欧美中在线观看| 亚洲黄色www网站| 婷婷六月综合网| 久久久久亚洲蜜桃| 日韩电影免费一区| 99热在线成人| 激情久久免费视频| 国产亚av手机在线观看| 99re6热在线精品视频播放| 成人在线视频一区二区三区| 91偷拍精品一区二区三区| 久久精品99久久久香蕉| 欧美日韩夫妻久久| 亚洲青青青在线视频| 国产激情精品久久久第一区二区 | 日本一级在线观看| 日韩中文字幕二区| 亚洲人成人77777线观看| 国产精品久久久久不卡| 深夜成人在线观看| 日韩精品一区二区三区在线播放 | 亚洲激情在线视频| 色综合天天性综合| 国产精品国产精品国产专区不蜜| 韩国女主播成人在线| 亚洲国产综合在线看不卡| 精品国产午夜肉伦伦影院| 浪潮色综合久久天堂| 国产激情视频在线| 久草在线官网| 亚洲77777| 青青青青在线视频| 亚洲国产一区二区在线| 国产精品二区在线观看| 国产精品xxxxx| 91精品成人久久| 欧美乱妇高清无乱码| 亚洲欧美日韩高清| 欧美tickling网站挠脚心| 欧美午夜不卡视频| 欧美日韩国产区| 亚洲激情av在线| 中文字幕亚洲一区二区av在线| 91在线观看高清| 成人美女视频在线观看18| 韩国精品在线观看| 美女性感视频久久| 久久精品一区二区国产| 亚洲高清电影| 狠狠久久婷婷| 国产精品v一区二区三区| 99久久综合狠狠综合久久aⅴ| 中文字幕精品影院| 欧美久久精品| 欧美成人午夜77777| 极品国产人妖chinesets亚洲人妖 激情亚洲另类图片区小说区 | 日本www.色| a级黄色一级片| av网站手机在线观看| 欧美黄网在线观看| 免费的一级黄色片| 欧美a级黄色大片| 国产911在线观看| 国产精品三级一区二区| 看一级黄色录像| 18黄暴禁片在线观看| 欧美亚洲色图视频| 欧美黑人经典片免费观看| 日韩av三级在线| 亚洲 中文字幕 日韩 无码| 丁香啪啪综合成人亚洲| 日韩福利视频在线| 男女午夜刺激视频| 日韩欧美亚洲| 最新国产在线| 三级黄视频在线观看| 青青草娱乐在线| h网站在线免费观看| 成人短视频在线| 蜜桃麻豆av在线| 韩国理伦片久久电影网| 成人在线日韩| 婷婷精品在线观看| 久久在线视频| 亚洲视频观看| 日本va欧美va欧美va精品| 国产成人精品三级麻豆| 久久精品男人的天堂| 亚洲激情一二三区| 欧美中文字幕亚洲一区二区va在线| 欧美精品丝袜中出| 亚洲国产精品va在线看黑人动漫| 亚洲人成网7777777国产| 久久深夜福利免费观看| 久久久免费在线观看| 国产精品第一区| 国产精品久久久久久久久久直播| 日本在线播放一区| 国产精品12345| 337p日本| 国产高清在线| 小视频免费在线观看| 99香蕉久久| 国产一区二区三区四区老人| 久久99热99| 中文字幕国产一区二区| 日韩欧美亚洲一二三区| 精品久久久久av影院| 欧美成人国产va精品日本一级| 国产精品午夜一区二区欲梦| 日本一区视频在线观看| av免费在线播放网站| 性色视频在线| 2019中文字幕在线电影免费 | 亚洲人体偷拍| 国产高清精品在线| 最新日韩在线视频| 欧美日韩日日摸| 色噜噜久久综合伊人一本| 国产精品久久久久福利| 欧美高清视频一区二区三区在线观看| 激情五月六月婷婷| 国产超碰在线观看| 国语对白在线刺激| 日韩在线麻豆| 三级一区在线视频先锋 | 最新精品国偷自产在线| 久久久人人人| 亚洲欧洲国产日韩|