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

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程语言 > java >内容正文

java

Java语言 泛型 类型擦除

發(fā)布時間:2025/3/21 java 16 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Java语言 泛型 类型擦除 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

初學(xué)者只要學(xué)習(xí)了"Java 編程簡介學(xué)習(xí)路徑"的第 20 單元,也可以學(xué)習(xí)本文。

此文將定義類型擦除,它與 Java 泛型的關(guān)系,以及未正確使用泛型時看到的一些神秘錯誤和警告消息(相信我,我們都經(jīng)歷過這些)。

前提條件

Java 編程語言的基礎(chǔ)知識

  • 更多信息:?《Java 編程簡介》學(xué)習(xí)路徑,第 1 單元

Java 泛型的初中級知識(第 20 單元)

  • 更多信息:?《Java 編程簡介》學(xué)習(xí)路徑,第 3 單元

逐步介紹

設(shè)置開發(fā)環(huán)境

要完成此文,需要安裝 JDK 和 Eclipse IDE。假設(shè)您擁有一定的 Java 基礎(chǔ)知識。如果沒有,請查閱 IBM developerWorks 上的?《Java 編程簡介》學(xué)習(xí)路徑?。

您還需要一個正常工作的開發(fā)環(huán)境。如果已有一個 Java 開發(fā)環(huán)境,可跳到第 2 步。

否則,請參閱?《Java 編程簡介》學(xué)習(xí)路徑,第 2 單元?獲得逐步操作說明。如果需要更多幫助,本節(jié)中還有一些視頻可幫助您。

首先,下載 Java Development Kit (JDK) V8 并將它安裝在您的機(jī)器上。如果需要幫助,請觀看下面的視頻。

點(diǎn)擊查看視頻演示

?接下來,將 Eclipse IDE 安裝在計(jì)算機(jī)上。如果需要幫助,請觀看下面的視頻。

點(diǎn)擊查看視頻演示

設(shè)置并準(zhǔn)備好開發(fā)環(huán)境后,前進(jìn)到第 2 步,這一步將定義類型擦除。

定義類型擦除

我們在編寫 Java 代碼時都會犯錯,在犯錯時,Java 編譯器會提供警告和錯誤消息。但有時,從 Java 編譯器獲得的信息有些晦澀難懂,尤其是在使用 Java 泛型時(除非您已了解類型擦除)。

在此文中,將會展示您將看到的與 Java 泛型相關(guān)的一些最常見警告和錯誤,以及如何避免或修復(fù)它們。首先,我們需要定義泛型工作原理背后的重要概念,那就是類型擦除。
類型擦除是 Java 編譯器用來支持使用泛型的一項(xiàng)技術(shù)。在?《Java 編程簡介》學(xué)習(xí)路徑的第 20 單元中,我展示了如何使用 Java 泛型,您已在其中了解了如何創(chuàng)建參數(shù)化的類和方法。我沒有真正談?wù)擃愋筒脸?#xff0c;因?yàn)樗且粋€非常復(fù)雜的主題,而且如果正確使用 Java 泛型,實(shí)際上不需要理解它。

如果編寫的 Java 代碼足夠長,就會看到我將展示的部分或所有消息。完成此文 后,您應(yīng)能理解這些消息的含義,以及如何永遠(yuǎn)避免它們!
使用泛型定義參數(shù)化的類時,Java 編譯器不會實(shí)際創(chuàng)建一個新類型(出于各種深層的技術(shù)原因,這里不會詳細(xì)解釋)。編譯器會接受您指定的 類型,將它擦除并替換回以下兩種類型之一:上限(如果您已指定)或 Object (如果沒指定)。考慮這個示例:

1

2

3

4

5

6

7

8

9

public class ObjectContainer<T> {

????private T contained;

public ObjectContainer(T contained) {

????this.contained = contained;

}

public T? getContained() {

????return contained;

}

}

在這個示例中,聲明參數(shù)化類型 ObjectContainer 時未指定上限,所以編譯器生成以下代碼:

1

2

3

4

5

6

7

8

9

public class ObjectContainer {

????private Object contained;

public ObjectContainer(Object contained) {

????this.contained = contained;

}

public Object getContained() {

????return contained;

}

}

因?yàn)闆]有上限,參數(shù)的類型 ( T ) 被擦除并替換回 Object 。在聲明 ObjectContainer 時,編譯器插入了一個強(qiáng)制轉(zhuǎn)換,所以代碼類似于:

1

2

3

ObjectContainer<Person> personContainer = new ObjectContainer<>(new Person("Steve", 49));

?????????????????????????????Person contained = personContainer.getContained();

System.out.println("ObjectContainer<Person> contains: " +contained.toString());

但是,編譯器生成以下代碼:

1

2

3

ObjectContainer personContainer = new ObjectContainer(new Person("Steve",49));

???????????????Person contained = (Person)personContainer.getContained();

System.out.println("ObjectContainer<Person> contains: " +contained.toString());

請注意上面代碼中轉(zhuǎn)換為 Person 的強(qiáng)制轉(zhuǎn)換。這是因?yàn)?#xff0c;編譯器在幕后將聲明的類型 ( Person ) 擦除并替換回 Object ,必須插入強(qiáng)制轉(zhuǎn)換,代碼才能正確運(yùn)行。

使用限定類型時,就會出現(xiàn)類似情況,除非使用指定的上限,而不是使用 Object 作為上限。

考慮下面的代碼:

1

2

3

4

5

6

7

8

9

public class ObjectContainer<T extends Person> {

????private T contained;

public ObjectContainer(T contained) {

????this.contained =contained;

}

public T getContained() {

????return contained;

}

}

在這種情況下,編譯器生成以下代碼:

1

2

3

4

5

6

7

8

9

public class ObjectContainer {

???private Person contained;

public ObjectContainer(Person contained) {

???this.contained = contained;

}

public Person getContained() {

???return contained;

}

}

擦除參數(shù)的類型 ( T extends Person ) 并替換回 Person ,后者是上限。聲明 ObjectContainer<Employee> 時,編譯器插入了一個強(qiáng)制轉(zhuǎn)換,所以代碼類似于:

1

2

3

ObjectContainer<Employee> personContainer = new ObjectContainer<>(new Employee("Steve", 49, "EMP001"));

?????????????????????????????Employee contained = personContainer.getContained();

System.out.println("ObjectContainer<Employee> contains: " + contained.toString());

但是,編譯器生成以下代碼:

1

2

3

ObjectContainer<Employee> personContainer = new ObjectContainer<>(new Employee("Steve", 49, "EMP001"));

???????????????Employee<String> contained = (Employee)personContainer.getContained();

System.out.println("ObjectContainer<Employee> contains: " + contained.toString());

解決錯誤

泛型通常很容易使用。除了在個別情況下。在我的經(jīng)驗(yàn)中,當(dāng)我嘗試執(zhí)行從面向?qū)ο蠼嵌戎v合理、但不受泛型支持的操作(通常為?協(xié)變?)時,就會出現(xiàn)這種情況。

接下來的 3 節(jié)將介紹兩種錯誤和一種警告,如果您使用的泛型足夠長,肯定會在某處看到這些錯誤和警告。看到它們后,您就會知道如何修復(fù)問題。

首先介紹錯誤。遇到這些錯誤時(您一定會遇到),您要知道發(fā)生了什么,這樣才能修復(fù)它們。

然后將介紹比任何其他與泛型相關(guān)的警告更常見的警告。遇到此警告時(您一定會遇到),您就會知道該做什么。

錯誤 1 - “Erasure of method xyz(…) is the same as another method in type Abc”

考慮下面的代碼:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

public class App {

public int process(List<Person> people) {

????for (Person person : people) {

????log.info("Processing person: " + person.toString());

}

????return person.size();

}

public int process(List<Employee> employees) {

????for (Employee employee :employees) {

????log.info("Processing employee: " + employee.toString());

}

????return employees.size();

}

}

上面的代碼初看起來很正常(比如 process() 只一個重載的方法),但是當(dāng)您編譯它時,會獲得以下消息:

Erasure of method process(List<Person>) is the same as another method in type App Erasure of method process(List<Employee>) is the same as another method in type App

發(fā)生了什么?這些方法有不同的方法簽名,所以重載了 process() 方法,對嗎?不對。回憶一下第 2 步,使用泛型時,(在本例中)編譯器擦除了 <> 中指定的類型并替換回 Object 。編譯器生成的代碼類似于:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

public class App {

public int process(List people) {

????for (Person person : people) {

????log.info("Processing person: " + person.toString());

}

????return person.size();

}

public int process(List employees) {

????for (Employee employee : employees) {

????log.info("Processing employee: " + employee.toString());

}

????return employees.size();

}

}

現(xiàn)在,存在的問題顯而易見。兩個具有相同簽名 ( process(List) ) 的方法無法在同一個類中共存。

知道編譯器如何擦除類型后,可以稍微更改一下設(shè)計(jì)來修復(fù)該問題:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

public class App {

public int processPeople(List<Person>people) {

????for (Person person : people) {

????log.info("Processing person: " + person.toString());

}

????return person.size();

}

public int processEmployees(List<Employee> employees) {

????for (Employee employee : employees) {

????log.info("Processing employee: " + employee.toString());

}

????return employees.size();

}

}

現(xiàn)在,代碼能正常編譯,方法名稱更準(zhǔn)確地反映了它們實(shí)際執(zhí)行的操作。

錯誤 2 - “The method xyz(Foo) in the type Abc is not applicable for the arguments (Foo)”

通常,會在以下情況下看到此錯誤: A 是 B 的超類,而且似乎可以合理地假設(shè)泛型類型 Foo<B> 是 Foo<A> 的子類(或者行為上類似子類,也就是說,具有協(xié)變行為)。但是,Java 泛型沒有協(xié)變性,可以認(rèn)為盡管 B 是 A 的子類,但 SomeGenericType<B> 既不是 SomeGenericType<A> 的子類,行為也不像子類。

基本上講,此錯誤與我們已在方法名稱上看到的錯誤非常相似,但此錯誤適用于方法參數(shù)。基礎(chǔ)問題相同。

考慮下面的代碼(備注: Employee 是 Person 的子類):

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

public class App {

public int processPeople(List<Person> people){

????for (Person person : people) {

????log.info("Processing person: " + person.toString());

}

????return person.size();

}

..

}

..

????List<Employee>employees;

????employees = new ArrayList<>();

????employees.add(employee1);

????employees.add(employee2);

????App app = new App();

????// ERROR ON NEXT LINE!

????app.processPeople(employees);..

對 App.processPeople(List<Employee>) 的調(diào)用生成以下錯誤消息:

The method processPeople(List<Person>) in the type App is not applicable for the arguments
(List<Employee>)

最初,這似乎是合理的,因?yàn)?Employee 是 Person 的子類,我們可以將一個 List<Employee> 傳遞給一個需要 List<Person> 的方法,對嗎?

不對。由于類型擦除, List<Person> 和 List<Employee> 被擦除并替換回 List 。(在我看來)該消息讓人困惑,而且應(yīng)提及錯誤的擦除方面(就像我們看到的第一個錯誤一樣)。

因?yàn)橐巡脸擃愋筒⑻鎿Q回 Object ,所以您可能認(rèn)為編譯器會允許這種情況通過檢測。但事實(shí)是,編譯器知道,由于類型擦除, List<Employee> 不是 List<Person> 的合適替代。擦除類型后,有關(guān)實(shí)際參數(shù)類型的信息就會丟失,而且允許編譯此代碼會導(dǎo)致運(yùn)行時問題。

那么如何修復(fù)此問題?也可以專門使用(或創(chuàng)建)一個方法來處理(之前示例中的) Employee 。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

public class App {

public int processPeople(List<Person> people){

????for (Person person : people) {

????log.info("Processing person: " + person.toString());

?}

????return person.size();

}

public int processEmployees(List<Employee> employees) {

????for (Employee employee : employees) {

????log.info("Processing employee: " + employee.toString());

}

????return employees.size();

}

}..

????List<Employee> employees;

????employees = new ArrayList<>();

????employees.add(employee1);

????employees.add(employee2);

????App app = new App();

?// ERROR ON NEXT LINE!

????app.processEmployees(employees);..

?但是,如果沒有 processEmployees() 方法,而且 Person 與 Employee 之間實(shí)際共享了"process person"的邏輯,該怎么辦?可以將 processPeople() 的簽名更改為:

1

2

3

4

5

6

7

8

9

10

11

public int processPeople(List<? extends Person> people)...

.

.

.

List<Employee> employees;

employees = new ArrayList<>();

employees.add(employee1);

employees.add(employee2);

App app = new App();?

// THIS WORKS GREAT NOW!

app.processPeople(employees);

現(xiàn)在,編譯器認(rèn)為類型參數(shù)的上限為 Person ,并在它生成的代碼中使用 Person (而不是 Object ),而且代碼運(yùn)行正常。

警告 - “Foo is a raw type.References to generic type Foo should be parameterized”

警告不會阻止程序運(yùn)行,但獲得警告就表明代碼中的某處可能存在錯誤。看到類似這樣的警告時,知道發(fā)生了什么會對您有所幫助,這樣您就可以知道代碼是正常的還是會在某個時刻引發(fā)問題。

泛型被設(shè)計(jì)為向后兼容原始類型(例如,對 List<T> 的引用兼容 List )。但是,您編寫的任何使用泛型的新代碼都絕不應(yīng)該使用原始類型。

為什么?像這樣通過引用在非參數(shù)化的泛型類型上調(diào)用方法是很危險(xiǎn)的。它可能導(dǎo)致?堆污染?等問題,我稍后將展示。

考慮下面的代碼:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

public class ObjectContainer<T> {

????private T contained;

public ObjectContainer(T contained) {

????this.contained = contained;

}

public T getContained() {

????return contained;

}

public void setContained(T contained){

????this.contained = contained;

}

@Override public String toString() {

????return contained.toString();

}

}

...

public class PersonContainer extends ObjectContainer<Person> {

public PersonContainer(Person contained) {

????super(contained);

}

@Override public void setContained(Person contained) {

????super.setContained(contained); }

}

..

PersonContainer pc = new PersonContainer(new Person("Test", 23));

????ObjectContainer oc = pc;

// WARNING occurs here System.out.println("PersonContainer (through ObjectContainer): " + oc.toString());

我引用的警告出現(xiàn)在我指定的位置上面的行上。在這里,準(zhǔn)確的警告是:

ObjectContainer is a raw type.References to generic type ObjectContainer<T> should be?
parameterized

使用原始泛型類型時,可能發(fā)生糟糕的事情。您可能想知道會發(fā)生哪些糟糕的事情。請繼續(xù)閱讀。

考慮下面這段代碼(它是在上一節(jié)的 ObjectContainer 定義的基礎(chǔ)上構(gòu)建的):

由于類型擦除, PersonContainer 不再通過 setContained() 方法來獲得多態(tài)性。請記住,泛型類型被擦除并替換回它的上限,所以 ObjectContainer 實(shí)際上看起來類似于:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

public class ObjectContainer {

????private Object contained;

public ObjectContainer(Object contained) {

????this.contained = contained;

}

public Object getContained() {

????return contained;

}

public void setContained(Object contained) {

????this.contained = contained;

}

@Override public String toString() {

????return contained.toString();

}

}

目前一切順利,但問題仍然存在,因?yàn)槲以?PersonContainer 中提供了一個接受 Person 對象的 setContained() 版本。現(xiàn)在, PersonContainer 和它的超類 ObjectContainer 之間的 setContained() 簽名是不同的。初看起來,似乎重寫了 setContained() ,事實(shí)并非如此。

為了保留多態(tài)性,編譯器為 PersonContainer 生成 setContained()?橋接方法?的重寫版本,所以 PersonContainer 實(shí)際上看起來類似于:

1

2

3

4

5

6

7

8

9

10

11

12

13

public class PersonContainer extends ObjectContainer<Person> {

public PersonContainer(Person contained) {

????super(contained);

?}

//Bridge method generated by the compiler – you never see this method

//(unless there is a problem)

public void setContained(Object contained) {

????setContained((Person)contained);

}

@Override public void setContained(Person contained) {

????super.setContained(contained);

}

}

現(xiàn)在考慮如果運(yùn)行此代碼會發(fā)生什么 - 假設(shè)我將它放在一個測試方法中:

1

2

3

4

5

6

7

8

9

@Test

@DisplayName("Testing PersonContainer - will throw ClassCastException")

public void testSetContainedPerson() {

????????????????PersonContainer pc = new PersonContainer(new Person("Test", 23));

????????????????ObjectContainer oc = pc;

// WARNING occurs here

????????????????System.out.println("PersonContainer (through ObjectContainer): " + oc.toString());

?// ClassCastException.Not good.assertThrows(ClassCastException.class, () -> oc.setContained("Howdy!"));

}

這就是我們所說的堆污染。堆污染不是好事。

類型擦除支持泛型類型的向后兼容,但如果未正確使用泛型,可能導(dǎo)致各種各樣的煩人問題。

現(xiàn)在您已更深入地了解了類型擦除,在遇到與泛型相關(guān)的錯誤和警告消息時,您將能更好地處理它們。

使用泛型時的最佳經(jīng)驗(yàn)規(guī)則是:堅(jiān)決不讓泛型相關(guān)警告悄然存在。編譯器會提醒您未正確使用泛型,而且您應(yīng)認(rèn)真留意到這些警告。

解決錯誤(視頻)

我創(chuàng)建了一個視頻來演練前幾節(jié)中的代碼,指出我們看到的各種錯誤,以及您不當(dāng)使用泛型時,編譯器為了提醒"危險(xiǎn)"而發(fā)出的一些警告。

在該視頻中,我展示了如何:

  • 克隆包含此 recipe 的代碼的 Github 存儲庫。
  • 將基于 Github 中的代碼的新 Maven 項(xiàng)目導(dǎo)入 Eclipse 中。
  • 演示:
    • 錯誤 1 和錯誤 2,
    • 忽略警告時會發(fā)生什么(糟糕的事情!)
    • 一個額外警告,如果忽略該警告,則會發(fā)生其他糟糕的事情!

點(diǎn)擊查看視頻演示

后續(xù)行動

網(wǎng)上與類型擦除和 Java 泛型相關(guān)的資源有許多。本節(jié)給出了我最喜歡的一些資源。盡情閱讀吧!

Angelika Langer – 類型擦除

Oracle 文檔 – 類型擦除

相關(guān)主題

  • 了解泛型

from:https://www.ibm.com/developerworks/cn/java/java-language-type-erasure/index.html

總結(jié)

以上是生活随笔為你收集整理的Java语言 泛型 类型擦除的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。

主站蜘蛛池模板: 中文字幕在线播放一区 | 一区二区三区日韩视频 | 大肉大捧一进一出好爽mba | 日韩欧美手机在线 | 国产精品一区二区在线 | 欧美黄色a视频 | 丝袜熟女一区二区 | caoporen在线| av大西瓜| 亚洲乱熟女一区二区三区小说 | 日韩一区二区免费在线观看 | 韩漫动漫免费大全在线观看 | 一区在线观看视频 | 日韩三级网 | 日本丰满肉感bbwbbwbbw | 调教撅屁股啪调教打臀缝av | 超碰在线人人草 | 喷水av | 91精品国产高潮对白 | 国产亚洲精品久久久久久777 | 成人免费看黄 | 原创少妇半推半就88av | 欧美十大老熟艳星 | 美女黄视频网站 | 国产黄a | 台湾色综合 | 久操不卡 | 中文字幕人妻一区二区 | 欧美成人影院 | 一级做a免费视频 | 精品国内自产拍在线观看视频 | 成人四色 | www.日韩.com | 日韩精品一区二区三 | 看免费黄色片 | 免费一级特黄特色毛片久久看 | 99re6这里只有精品 | 亚洲av成人一区二区国产精品 | 一区二区三区在线观 | 国产的av| 九一在线观看免费高清视频 | 欧美视频精品 | 亚洲欧美精品一区二区 | 欧美福利电影 | 成人午夜影片 | 狠狠爱亚洲 | 美女网站免费观看视频 | 久久午夜视频 | 伊人影院在线视频 | 一吻定情2013日剧 | 国产精品-区区久久久狼 | 小视频在线免费观看 | 午夜视频免费观看 | 久久午夜电影 | 法国空姐在线观看免费 | 粉嫩欧美一区二区三区 | 欧美成人一级 | 深夜福利1000 | 台湾a级艳片潘金莲 | 黄色录像a级片 | 日本老肥婆bbbwbbbwzr | 精品亚洲成人 | 亚洲精品1区2区 | 亚洲精品日韩精品 | 免费的av | 无码少妇一区二区三区 | 国产精品久久久久久一区 | 日韩综合精品 | 99久免费精品视频在线观78 | 日本在线视频中文字幕 | www国产精品内射老熟女 | 手机在线看片日韩 | 蛇女欲潮性三级 | 日本黄视频在线观看 | 久久久久久久久网站 | 免费视频一二三区 | 欧亚乱熟女一区二区在线 | 亚洲一区二区三区免费在线观看 | 天堂免费在线视频 | 手机在线永久免费观看av片 | 日本色视| 黄色一级黄色片 | 日韩欧美激情在线 | 精品国产乱码一区二区三区99 | 色婷婷久久 | 男女作爱免费网站 | 婷综合| 欧洲av在线播放 | 黑人巨大精品欧美一区二区免费 | 波多野结衣中文字幕在线播放 | 性――交――性――乱睡觉 | 91不卡在线| 日韩一区网站 | 色婷婷天堂 | 欧美日韩高清在线观看 | 在线播放国产一区 | 他揉捏她两乳不停呻吟动态图 | 全程粗话对白视频videos | 久久久亚洲一区 |