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

歡迎訪問 生活随笔!

生活随笔

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

编程问答

擦除

發布時間:2024/4/17 编程问答 49 豆豆
生活随笔 收集整理的這篇文章主要介紹了 擦除 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

Java泛型-類型擦除

一、概述

?? ???Java泛型在使用過程有諸多的問題,如不存在List<String>.class, List<Integer>不能賦值給List<Number>(不可協變),奇怪的ClassCastException等。 正確的使用Java泛型需要深入的了解Java的一些概念,如協變,橋接方法,以及這篇筆記記錄的類型擦除。Java泛型的處理幾乎都在編譯器中進行,編譯器生成的bytecode是不包涵泛型信息的,泛型類型信息將在編譯處理是被擦除,這個過程即類型擦除。

二、編譯器如何處理泛型?

?? ??通常情況下,一個編譯器處理泛型有兩種方式:
?? ? 1.Code specialization。在實例化一個泛型類或泛型方法時都產生一份新的目標代碼(字節碼or二進制代碼)。例如,針對一個泛型list,可能需要 針對string,integer,float產生三份目標代碼。
?? ? 2.Code sharing。對每個泛型類只生成唯一的一份目標代碼;該泛型類的所有實例都映射到這份目標代碼上,在需要的時候執行類型檢查和類型轉換。
?? ? C++中的模板(template)是典型的Code specialization實現。C++編譯器會為每一個泛型類實例生成一份執行代碼。執行代碼中integer list和string list是兩種不同的類型。這樣會導致代碼膨脹(code bloat),不過有經驗的C++程序員可以有技巧的避免代碼膨脹。
?? ? Code specialization另外一個弊端是在引用類型系統中,浪費空間,因為引用類型集合中元素本質上都是一個指針。沒必要為每個類型都產生一份執行代碼。而這也是Java編譯器中采用Code sharing方式處理泛型的主要原因。
?? ? Java編譯器通過Code sharing方式為每個泛型類型創建唯一的字節碼表示,并且將該泛型類型的實例都映射到這個唯一的字節碼表示上。將多種泛型類形實例映射到唯一的字節碼表示是通過類型擦除(type erasue)實現的。

三、?什么是類型擦除?

?? ??類型擦除指的是通過類型參數合并,將泛型類型實例關聯到同一份字節碼上。編譯器只為泛型類型生成一份字節碼,并將其實例關聯到這份字節碼上。類型擦除的關鍵在于從泛型類型中清除類型參數的相關信息,并且再必要的時候添加類型檢查和類型轉換的方法。
?? ??類型擦除可以簡單的理解為將泛型java代碼轉換為普通java代碼,只不過編譯器更直接點,將泛型java代碼直接轉換成普通java字節碼。
?? ??類型擦除的主要過程如下:
?? ? 1.將所有的泛型參數用其最左邊界(最頂級的父類型)類型替換。
?? ? 2.移除所有的類型參數。
?? ??如
interface Comparable?<A>?{?
? public int compareTo(?A?that);?
}?
final class NumericValue implements Comparable?<NumericValue>?{?
? priva?te byte value;??
? public??NumericValue?(byte value) { this.value = value; }??
? public? byte getValue() { return value; }??
? public? int compareTo(?NumericValue?t?hat) { return this.value - that.value; }?
}?
-----------------
class Collections {??
? public static?<A extends Comparable<A>>A?max(Collection?<A>?xs) {?
??? Iterator?<A>?xi = xs.iterator();?
????A?w = xi.next();?
??? while (xi.hasNext()) {?
??????A?x = xi.next();?
????? if (w.compareTo(x) < 0) w = x;?
??? }?
??? return w;?
? }?
}?
final class Test {?
? public static void main (String[ ] args) {?
??? LinkedList?<NumericValue>?numberList?= new LinkedList?<NumericValue>?();?
????numberList?.add(new NumericValue((byte)0));??
????numberList?.add(new NumericValue((byte)1));??
??? NumericValue y = Collections.max(?numberList?);??
? }?
}
經過類型擦除后的類型為
?interface Comparable {?
? public int compareTo(?Object?that);?
}?
final class NumericValue implements Comparable {?
? priva?te byte value;??
? public??NumericValue?(byte value) { this.value = value; }??
? public? byte getValue() { return value; }??
? public? int compareTo(?NumericValue?t?hat)?? { return this.value - that.value; }?
??public? int compareTo(Object that) { return this.compareTo((NumericValue)that);? }?
}?
-------------
class Collections {??
? public static?Comparable?max(Collection xs) {?
??? Iterator xi = xs.iterator();?
????Comparable?w =?(Comparable)?xi.next();?
??? while (xi.hasNext()) {?
??????Comparable?x =?(Comparable)?xi.next();?
????? if (w.compareTo(x) < 0) w = x;?
??? }?
??? return w;?
? }?
}?
final class Test {?
? public static void main (String[ ] args) {?
??? LinkedList?numberList?= new LinkedList();?
????numberList?.add(new NumericValue((byte)0));??,
????numberList?.add(new NumericValue((byte)1));??
??? NumericValue y =?(NumericValue)?Collections.max(?numberList?);??
? }?
}
第一個泛型類Comparable <A>擦除后?A被替換為最左邊界Object。Comparable<NumericValue>的類型參數NumericValue被擦除掉,但是這直 接導致NumericValue沒有實現接口Comparable的compareTo(Object that)方法,于是編譯器充當好人,添加了一個橋接方法。
第二個示例中限定了類型參數的邊界<A extends Comparable<A>>A,A必須為Comparable<A>的子類,按照類型擦除的過程,先講所有的類型參數?ti換為最左邊界Comparable<A>,然后去掉參數類型A,得到最終的擦除后結果。

四、類型擦除帶來的問題

?? ??正是由于類型擦除的隱蔽存在,直接導致了眾多的泛型靈異問題。
?Q1.用同一泛型類的實例區分方法簽名?——NO!
??? import java.util.*;

??? public class Erasure{

??? ??? ??? public void test(List<String> ls){
??????? ??? ??? System.out.println("Sting");
??? ??? ??? }
??? ??? ??? public void test(List<Integer> li){
??????? ??? ??? System.out.println("Integer");
??? ??? ??? }
??? }
編譯該類,

?

參數類型明明不一樣啊,一個List<String>,一個是List<Integer>,但是,偷偷的說,type erasure之后,它就都是List了??
Q2.?同時catch同一個泛型異常類的多個實例?——NO!
同理,如果定義了一個泛型一場類GenericException<T>,千萬別同時catch GenericException<Integer>?和GenericException<String>,因為他們是一樣一樣滴??
Q3.泛型類的靜態變量是共享的?——Yes!
猜猜這段代碼的輸出是什么?
import java.util.*;

public class StaticTest{
??? public static void main(String[] args){
??????? GT<Integer> gti = new GT<Integer>();
??????? gti.var=1;
??????? GT<String> gts = new GT<String>();
??????? gts.var=2;
??????? System.out.println(gti.var);
??? }
}
class GT<T>{
??? public static int var=0;
??? public void nothing(T x){}
}
答案是——2!由于經過類型擦除,所有的泛型類實例都關聯到同一份字節碼上,泛型類的所有靜態變量是共享的。

五、Just remember

1.虛擬機中沒有泛型,只有普通類和普通方法
2.所有泛型類的類型參數在編譯時都會被擦除
3.創建泛型對象時請指明類型,讓編譯器盡早的做參數檢查(Effective Java,第23條:請不要在新代碼中使用原生態類型)
4.不要忽略編譯器的警告信息,那意味著潛在的ClassCastException等著你。

總結

以上是生活随笔為你收集整理的擦除的全部內容,希望文章能夠幫你解決所遇到的問題。

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