偷偷摘套内射激情视频,久久精品99国产国产精,中文字幕无线乱码人妻,中文在线中文a,性爽19p

你,可能沒完全搞懂 Java 泛型

開發(fā) 后端
今天我們來談?wù)劮盒?。其?shí)在初學(xué)的時(shí)候,我就對泛型有點(diǎn)蒙,因?yàn)榭吹接腥苏f Java 的泛型不是真的泛型,我搞不懂。

[[437732]]

本文轉(zhuǎn)載自微信公眾號「yes的練級攻略」,作者是yes呀。轉(zhuǎn)載本文請聯(lián)系yes的練級攻略公眾號。

大家好,我是yes。

今天我們來談?wù)劮盒?。其?shí)在初學(xué)的時(shí)候,我就對泛型有點(diǎn)蒙,因?yàn)榭吹接腥苏f Java 的泛型不是真的泛型,我搞不懂。

還有人說 Java 的泛型在實(shí)際運(yùn)行時(shí)候會把類型給擦除了,我想著擦除是什么意思?為什么要擦除?

那把類型給擦除了為什么反射的時(shí)候還能得到泛型的類型信息?

我們今天就來盤一盤泛型:

  • 為什么需要泛型?
  • 為什么都說Java的泛型是偽泛型?
  • 為什么Java泛型的實(shí)現(xiàn)是類型擦除?
  • 既然擦除了類型,為什么在運(yùn)行期仍能反射獲得類型?

話不多說,發(fā)車!

為什么需要泛型

我們都知道在 Java5 之前是沒有泛型的,沒泛型都能用的好好的,那為什么要加個(gè)泛型呢,能給我們帶來什么呢?

我們先來看下下面這段代碼:

  1. List list = new ArrayList(); 
  2.  list.add("yes"); // 加入string 
  3.  list.add(233); // 加入int 

在沒有泛型的時(shí)候,加入的集合的數(shù)據(jù)并不會做任何約束,都會被當(dāng)作成 Object 類型。

可能有人說,這很好呀,多自由!確實(shí),自由是自由了,但是代碼的約束能力越低,就越容易出錯(cuò),使用上也有諸多不便,比如獲取的時(shí)候需要強(qiáng)轉(zhuǎn)。

如果一不小心取錯(cuò)類型,編譯的時(shí)候能過,但是運(yùn)行的時(shí)候卻拋錯(cuò)。

綜上,Java 引入了泛型。

而泛型的作用就是加了一層約束,約束了類型。

有了這一層約束就好辦事兒了,由于聲明了類型,可以在編譯的時(shí)候就識別出不準(zhǔn)確的類型元素。使得錯(cuò)誤提早拋出,避免運(yùn)行時(shí)才發(fā)現(xiàn)。

并且也不需要在代碼上顯示的強(qiáng)轉(zhuǎn),從以下代碼可以看出,能直接獲取 String 類型元素。

我們再小結(jié)一下泛型的好處:

提高了代碼的可讀性,一眼就能看出集合(其它泛型類)的類型

可在編譯期檢查類型安全,增加程序的健壯性

省心不需要強(qiáng)轉(zhuǎn)(其實(shí)內(nèi)部幫做了強(qiáng)轉(zhuǎn),下面會說)

提高代碼的復(fù)用率,定義好泛型,一個(gè)方法(類)可以適配所有類型 (其實(shí)以前 Object 也行,就是比較麻煩)

為什么都說Java的泛型是偽泛型

看起來我們平日用的一些泛型好像沒啥毛病啊?為什么都說Java的泛型是偽泛型?哪里偽了?

我們再來看一段代碼:

可以看到,我聲明的是一個(gè) String 類型的集合,但是通過反射往集合中插入了 int 類型的數(shù)據(jù),居然成功了???

這說明在運(yùn)行時(shí)泛型根本沒有起作用!也就是說在運(yùn)行的時(shí)候 JVM 獲取不到泛型的信息,也會不對其做任何的約束。

你可以認(rèn)為 Java 的泛型就是編譯的時(shí)候生效,運(yùn)行的時(shí)候沒有泛型,所以大家才說 Java 是偽泛型!

因此,雖然在 IDE 寫代碼的時(shí)候泛型生效了,而實(shí)際上在運(yùn)行的時(shí)候泛型的類型是被擦除的。

一言蔽之,Java的泛型只在編譯時(shí)生效,JVM 運(yùn)行時(shí)沒有泛型。

為什么Java泛型的實(shí)現(xiàn)是類型擦除?

類型擦除 (type Erasure)。

Java 之所以在運(yùn)行時(shí)將類型擦除的原因是為了向下兼容,即兼容 Java5 之前的編譯的 class 文件。

例如 Java 1.2 上正在跑的代碼,可以在 Java 5 的 JRE 上運(yùn)行。

就是為了這該死的向下兼容,才使得 Java 實(shí)現(xiàn)的是偽泛型。

我從現(xiàn)有的實(shí)現(xiàn)倒推偽泛型的設(shè)計(jì)可能思路(我個(gè)人瞎掰的,您隨意聽聽)是這樣的:

  • 這 Java 5 以前的版本,線上已經(jīng)有很多應(yīng)用在跑了,我好像不能新加一套,影響推廣還可能被罵的很慘
  • 咋辦,泛型畢竟是加一個(gè)約束,以前的代碼沒這個(gè)約束啊,該如何兼容?
  • 有了,要不我在編譯器上動(dòng)手腳,在編譯的時(shí)候識別和約束泛型,然后編譯過了就把泛型的信息擦除了。這樣運(yùn)行的時(shí)候約束不是沒了嗎?不就和之前保持一致了嗎?好,就這樣干了!

總而言之,就是為了向下兼容才采用類型擦除來實(shí)現(xiàn)的。

這里還有個(gè)坑,也就是泛型不支持基本類型,比如 int。因?yàn)榉盒筒脸缶妥兂闪薕bject,這個(gè) int 和 Object 兼容有點(diǎn)麻煩。

我在網(wǎng)上看 R大的解釋如下:

GJ / Java 5說:這個(gè)問題有點(diǎn)麻煩,趕不及在這個(gè)版本發(fā)布前完成了,就先放著不管吧。于是Java 5的泛型就不支持原始類型,而我們不得不寫惡心的ArrayList、ArrayList…

這就是一個(gè)偷懶了的地方。

emmm,這說明啥?寫 Java 的也是程序員,也是要發(fā)版有上線需求的,所以說......

好了,言歸正傳,現(xiàn)在 Java 的泛型實(shí)現(xiàn)確實(shí)是偽泛型??吹竭@不經(jīng)有人會發(fā)問?難道就只能一直偽泛型了嗎?

那啥,我覺得吧,只要時(shí)間允許,只要錢夠,應(yīng)該都能做?哈哈哈。

既然擦除了類型,為什么在運(yùn)行期仍能反射獲得類型?

難道是沒擦干凈?別急,我們慢慢看。

我們先來回顧一下這段代碼:

我們定義了泛型類型為 String 的 list,并且獲取的 str 不需要強(qiáng)轉(zhuǎn),這一步是怎么做的呢?我們 javap -c 看下字節(jié)碼:

我們從反編譯看生成的字節(jié)碼可以看到, new 的 list 沒有保存泛型的信息,所以是被擦除了。

然后看到 #7 沒,有個(gè) checkcast ,強(qiáng)轉(zhuǎn)的類型是 String,看到這大伙兒應(yīng)該都明白,為什么類型擦除了,但是我們 get 的時(shí)候不需要強(qiáng)轉(zhuǎn)呢?因?yàn)榫幾g器隱性的幫我們插入了強(qiáng)轉(zhuǎn)的代碼!所以我們的 Java 代碼中不需要寫強(qiáng)轉(zhuǎn)。

再回到此小節(jié)標(biāo)題:既然擦除了類型,為什么在運(yùn)行期仍能反射獲得類型?

答案就藏在 class 文件中。我們來看下這段代碼:

通過反射,我確實(shí)獲得了 list 的類型。那既然類型被擦除了,這又是怎么做到的呢?

我們直接進(jìn)行一手 javap -v,反編譯看到字節(jié)碼里面有這樣的記錄:

這下很好理解了,class 文件里面存了這個(gè)信息,所以我們通過反射自然而然的就能得到這個(gè)類型。沒錯(cuò),就是這么簡單。

也正因?yàn)樵砣绱?,所以我們只能對以下三種情況利用反射獲取泛型類型:

  • 成員變量的泛型
  • 方法入?yún)⒌姆盒?/li>
  • 方法返回值的泛型

對于局部變量這種是無能為力的。

最后

好了,今天關(guān)于泛型的文章暫時(shí)先到這,其實(shí)泛型的東西還沒講完,比如通配符、上界下界的限制(泛型的 PECS 原則),再如泛型的橋接,以及橋接的坑。

東西還挺多的,所以放下篇!等著哈。

參考

 

https://www.zhihu.com/question/28665443/answer/118148143

 

責(zé)任編輯:武曉燕 來源: yes的練級攻略
相關(guān)推薦

2022-01-03 18:07:56

泛型場景demo

2021-02-08 11:20:27

Java類型數(shù)組

2021-03-01 07:34:42

Java泛型ArrayList

2021-10-17 13:10:56

函數(shù)TypeScript泛型

2024-04-23 08:23:36

TypeScript泛型Generics

2009-09-25 10:03:51

Java泛型

2021-07-09 05:56:28

云計(jì)算IaaS公有云

2011-06-03 08:49:54

Java

2021-12-30 19:34:15

Java泛型JDK

2025-07-03 08:23:45

2021-06-17 06:51:32

Java泛型Java編程

2021-09-29 18:17:30

Go泛型語言

2021-07-01 06:47:30

Java泛型泛型擦除

2011-03-21 16:26:28

java泛型

2021-06-18 08:25:42

Java泛型通配符

2022-03-02 14:00:46

Nest.jsExpress端口

2022-09-15 14:04:07

Go語言泛型

2017-03-06 16:51:52

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

2020-10-20 10:17:20

Java泛型Type

2021-10-29 10:55:07

Go 泛型語言
點(diǎn)贊
收藏

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