成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專(zhuān)欄INFORMATION COLUMN

Java? 教程(類(lèi)型擦除)

zsy888 / 2769人閱讀

類(lèi)型擦除

泛型被引入到Java語(yǔ)言中,以便在編譯時(shí)提供更嚴(yán)格的類(lèi)型檢查并支持通用編程,為了實(shí)現(xiàn)泛型,Java編譯器將類(lèi)型擦除應(yīng)用于:

如果類(lèi)型參數(shù)是無(wú)界的,則用它們的邊界或Object替換泛型類(lèi)型中的所有類(lèi)型參數(shù),因此,生成的字節(jié)碼僅包含普通的類(lèi)、接口和方法。

如有必要,插入類(lèi)型轉(zhuǎn)換以保持類(lèi)型安全。

生成橋接方法以保留擴(kuò)展泛型類(lèi)型中的多態(tài)性。

類(lèi)型擦除確保不為參數(shù)化類(lèi)型創(chuàng)建新類(lèi),因此,泛型不會(huì)產(chǎn)生運(yùn)行時(shí)開(kāi)銷(xiāo)。

泛型類(lèi)型擦除

在類(lèi)型擦除過(guò)程中,Java編譯器將擦除所有類(lèi)型參數(shù),并在類(lèi)型參數(shù)有界時(shí)將其每一個(gè)替換為第一個(gè)邊界,如果類(lèi)型參數(shù)為無(wú)界,則替換為Object

考慮以下表示單鏈表中節(jié)點(diǎn)的泛型類(lèi):

public class Node {

    private T data;
    private Node next;

    public Node(T data, Node next) {
        this.data = data;
        this.next = next;
    }

    public T getData() { return data; }
    // ...
}

因?yàn)轭?lèi)型參數(shù)T是無(wú)界的,所以Java編譯器用Object替換它:

public class Node {

    private Object data;
    private Node next;

    public Node(Object data, Node next) {
        this.data = data;
        this.next = next;
    }

    public Object getData() { return data; }
    // ...
}

在以下示例中,泛型Node類(lèi)使用有界類(lèi)型參數(shù):

public class Node> {

    private T data;
    private Node next;

    public Node(T data, Node next) {
        this.data = data;
        this.next = next;
    }

    public T getData() { return data; }
    // ...
}

Java編譯器將有界類(lèi)型參數(shù)T替換為第一個(gè)邊界類(lèi)Comparable

public class Node {

    private Comparable data;
    private Node next;

    public Node(Comparable data, Node next) {
        this.data = data;
        this.next = next;
    }

    public Comparable getData() { return data; }
    // ...
}
泛型方法擦除

Java編譯器還會(huì)擦除泛型方法參數(shù)中的類(lèi)型參數(shù),考慮以下泛型方法:

// Counts the number of occurrences of elem in anArray.
//
public static  int count(T[] anArray, T elem) {
    int cnt = 0;
    for (T e : anArray)
        if (e.equals(elem))
            ++cnt;
        return cnt;
}

因?yàn)?b>T是無(wú)界的,所以Java編譯器用Object替換它:

public static int count(Object[] anArray, Object elem) {
    int cnt = 0;
    for (Object e : anArray)
        if (e.equals(elem))
            ++cnt;
        return cnt;
}

假設(shè)定義了以下類(lèi):

class Shape { /* ... */ }
class Circle extends Shape { /* ... */ }
class Rectangle extends Shape { /* ... */ }

你可以編寫(xiě)一個(gè)泛型方法來(lái)繪制不同的形狀:

public static  void draw(T shape) { /* ... */ }

Java編譯器將T替換為Shape

public static void draw(Shape shape) { /* ... */ }
類(lèi)型擦除和橋接方法的影響

有時(shí)類(lèi)型擦除會(huì)導(dǎo)致你可能沒(méi)有預(yù)料到的情況,以下示例顯示了如何發(fā)生這種情況,該示例(在橋接方法中描述)顯示了編譯器有時(shí)如何創(chuàng)建一個(gè)稱(chēng)為橋接方法的合成方法,作為類(lèi)型擦除過(guò)程的一部分。

給出以下兩個(gè)類(lèi):

public class Node {

    public T data;

    public Node(T data) { this.data = data; }

    public void setData(T data) {
        System.out.println("Node.setData");
        this.data = data;
    }
}

public class MyNode extends Node {
    public MyNode(Integer data) { super(data); }

    public void setData(Integer data) {
        System.out.println("MyNode.setData");
        super.setData(data);
    }
}

考慮以下代碼:

MyNode mn = new MyNode(5);
Node n = mn;            // A raw type - compiler throws an unchecked warning
n.setData("Hello");     
Integer x = mn.data;    // Causes a ClassCastException to be thrown.

類(lèi)型擦除后,此代碼變?yōu)椋?/p>

MyNode mn = new MyNode(5);
Node n = (MyNode)mn;         // A raw type - compiler throws an unchecked warning
n.setData("Hello");
Integer x = (String)mn.data; // Causes a ClassCastException to be thrown.

以下是代碼執(zhí)行時(shí)發(fā)生的情況:

n.setData("Hello")導(dǎo)致方法setData(Object)在類(lèi)MyNode的對(duì)象上執(zhí)行(MyNode類(lèi)從Node繼承了setData(Object))。

setData(Object)的方法體中,n引用的對(duì)象的data字段被分配給String

通過(guò)mn引用的同一對(duì)象的data字段可以被訪問(wèn),并且應(yīng)該是一個(gè)整數(shù)(因?yàn)?b>mn是MyNode,它是Node)。

嘗試將String分配給Integer會(huì)導(dǎo)致Java編譯器在賦值時(shí)插入的轉(zhuǎn)換中出現(xiàn)ClassCastException。

橋接方法

在編譯擴(kuò)展參數(shù)化類(lèi)或?qū)崿F(xiàn)參數(shù)化接口的類(lèi)或接口時(shí),編譯器可能需要?jiǎng)?chuàng)建一個(gè)合成方法,稱(chēng)為橋接方法,作為類(lèi)型擦除過(guò)程的一部分,你通常不需要擔(dān)心橋接方法,但如果出現(xiàn)在堆棧跟蹤中,你可能會(huì)感到困惑。

在類(lèi)型擦除之后,Node和MyNode類(lèi)變?yōu)椋?/p>

public class Node {

    public Object data;

    public Node(Object data) { this.data = data; }

    public void setData(Object data) {
        System.out.println("Node.setData");
        this.data = data;
    }
}

public class MyNode extends Node {

    public MyNode(Integer data) { super(data); }

    public void setData(Integer data) {
        System.out.println("MyNode.setData");
        super.setData(data);
    }
}

在類(lèi)型擦除之后,方法簽名不匹配,Node方法變?yōu)?b>setData(Object),MyNode方法變?yōu)?b>setData(Integer),因此,MyNodesetData方法不會(huì)覆蓋NodesetData方法。

為了解決這個(gè)問(wèn)題并在類(lèi)型擦除后保留泛型類(lèi)型的多態(tài)性,Java編譯器生成一個(gè)橋接方法以確保子類(lèi)型按預(yù)期工作,對(duì)于MyNode類(lèi),編譯器為setData生成以下橋接方法:

class MyNode extends Node {

    // Bridge method generated by the compiler
    //
    public void setData(Object data) {
        setData((Integer) data);
    }

    public void setData(Integer data) {
        System.out.println("MyNode.setData");
        super.setData(data);
    }

    // ...
}

如你所見(jiàn),橋接方法與類(lèi)型擦除后的Node類(lèi)的setData方法具有相同的方法簽名,委托給原始的setData方法。

非具體化類(lèi)型

類(lèi)型擦除部分討論編譯器移除與類(lèi)型參數(shù)和類(lèi)型實(shí)參相關(guān)的信息的過(guò)程,類(lèi)型擦除的結(jié)果與變量參數(shù)(也稱(chēng)為varargs)方法有關(guān),該方法的varargs形式參數(shù)具有非具體化的類(lèi)型,有關(guān)varargs方法的更多信息,請(qǐng)參閱將信息傳遞給方法或構(gòu)造函數(shù)的任意數(shù)量的參數(shù)部分。

可具體化類(lèi)型是類(lèi)型信息在運(yùn)行時(shí)完全可用的類(lèi)型,這包括基元、非泛型類(lèi)型、原始類(lèi)型和無(wú)界通配符的調(diào)用。

非具體化類(lèi)型是指在編譯時(shí)通過(guò)類(lèi)型擦除移除信息的類(lèi)型,即未定義為無(wú)界通配符的泛型類(lèi)型的調(diào)用,非具體化類(lèi)型在運(yùn)行時(shí)不具有所有可用的信息。非具體化類(lèi)型的例子有ListList,JVM無(wú)法在運(yùn)行時(shí)區(qū)分這些類(lèi)型,正如對(duì)泛型的限制所示,在某些情況下不能使用非具體化類(lèi)型:例如,在instanceof表達(dá)式中,或作為數(shù)組中的元素。

堆污染

當(dāng)參數(shù)化類(lèi)型的變量引用不是該參數(shù)化類(lèi)型的對(duì)象時(shí),會(huì)發(fā)生堆污染,如果程序執(zhí)行某些操作,在編譯時(shí)產(chǎn)生未經(jīng)檢查的警告,則會(huì)出現(xiàn)這種情況。如果在編譯時(shí)(在編譯時(shí)類(lèi)型檢查規(guī)則的限制內(nèi))或在運(yùn)行時(shí),無(wú)法驗(yàn)證涉及參數(shù)化類(lèi)型(例如,強(qiáng)制轉(zhuǎn)換或方法調(diào)用)的操作的正確性,將生成未經(jīng)檢查的警告,例如,在混合原始類(lèi)型和參數(shù)化類(lèi)型時(shí),或者在執(zhí)行未經(jīng)檢查的強(qiáng)制轉(zhuǎn)換時(shí),會(huì)發(fā)生堆污染。

在正常情況下,當(dāng)所有代碼同時(shí)編譯時(shí),編譯器會(huì)發(fā)出未經(jīng)檢查的警告,以引起你對(duì)潛在堆污染的注意,如果多帶帶編譯代碼的各個(gè)部分,則很難檢測(cè)到堆污染的潛在風(fēng)險(xiǎn),如果確保代碼在沒(méi)有警告的情況下編譯,則不會(huì)發(fā)生堆污染。

具有非具體化形式參數(shù)的Varargs方法的潛在漏洞

包含vararg輸入?yún)?shù)的泛型方法可能會(huì)導(dǎo)致堆污染。

考慮以下ArrayBuilder類(lèi):

public class ArrayBuilder {

  public static  void addToList (List listArg, T... elements) {
    for (T x : elements) {
      listArg.add(x);
    }
  }

  public static void faultyMethod(List... l) {
    Object[] objectArray = l;     // Valid
    objectArray[0] = Arrays.asList(42);
    String s = l[0].get(0);       // ClassCastException thrown here
  }

}

以下示例HeapPollutionExample使用ArrayBuiler類(lèi):

public class HeapPollutionExample {

  public static void main(String[] args) {

    List stringListA = new ArrayList();
    List stringListB = new ArrayList();

    ArrayBuilder.addToList(stringListA, "Seven", "Eight", "Nine");
    ArrayBuilder.addToList(stringListB, "Ten", "Eleven", "Twelve");
    List> listOfStringLists =
      new ArrayList>();
    ArrayBuilder.addToList(listOfStringLists,
      stringListA, stringListB);

    ArrayBuilder.faultyMethod(Arrays.asList("Hello!"), Arrays.asList("World!"));
  }
}

編譯時(shí),ArrayBuilder.addToList方法的定義產(chǎn)生以下警告:

warning: [varargs] Possible heap pollution from parameterized vararg type T

當(dāng)編譯器遇到varargs方法時(shí),它會(huì)將varargs形式參數(shù)轉(zhuǎn)換為數(shù)組,但是,Java編程語(yǔ)言不允許創(chuàng)建參數(shù)化類(lèi)型的數(shù)組,在方法ArrayBuilder.addToList中,編譯器將varargs形式參數(shù)T...元素轉(zhuǎn)換為形式參數(shù)T[]元素,即數(shù)組,但是,由于類(lèi)型擦除,編譯器會(huì)將varargs形式參數(shù)轉(zhuǎn)換為Object[]元素,因此,存在堆污染的可能性。

以下語(yǔ)句將varargs形式參數(shù)l分配給Object數(shù)組objectArgs

Object[] objectArray = l;

這種語(yǔ)句可能會(huì)引入堆污染,與varargs形式參數(shù)l的參數(shù)化類(lèi)型匹配的值可以分配給變量objectArray,因此可以分配給l,但是,編譯器不會(huì)在此語(yǔ)句中生成未經(jīng)檢查的警告,編譯器在將varargs形式參數(shù)List ... l轉(zhuǎn)換為形式參數(shù)List[] l時(shí)已生成警告,此語(yǔ)句有效,變量l的類(lèi)型為List[],它是Object[]的子類(lèi)型。

因此,如果將任何類(lèi)型的List對(duì)象分配給objectArray數(shù)組的任何數(shù)組組件,編譯器不會(huì)發(fā)出警告或錯(cuò)誤,如下所示:

objectArray[0] = Arrays.asList(42);

此語(yǔ)句使用包含一個(gè)Integer類(lèi)型的對(duì)象的List對(duì)象分配objectArray數(shù)組的第一個(gè)數(shù)組組件。

假設(shè)你使用以下語(yǔ)句調(diào)用ArrayBuilder.faultyMethod

ArrayBuilder.faultyMethod(Arrays.asList("Hello!"), Arrays.asList("World!"));

在運(yùn)行時(shí),JVM在以下語(yǔ)句中拋出ClassCastException

// ClassCastException thrown here
String s = l[0].get(0);

存儲(chǔ)在變量l的第一個(gè)數(shù)組組件中的對(duì)象具有List類(lèi)型,但此語(yǔ)句需要一個(gè)List類(lèi)型的對(duì)象。

防止來(lái)自使用非具體化的形式參數(shù)的Varargs方法的警告

如果聲明一個(gè)具有參數(shù)化類(lèi)型參數(shù)的varargs方法,并確保方法體不會(huì)因?yàn)閷?duì)varargs形式參數(shù)的不正確處理而拋出ClassCastException或其他類(lèi)似異常,你可以通過(guò)向靜態(tài)和非構(gòu)造方法聲明添加以下注解來(lái)阻止編譯器為這些類(lèi)型的varargs方法生成的警告:

@SafeVarargs

@SafeVarargs注解是方法合約的文檔部分,這個(gè)注解斷言該方法的實(shí)現(xiàn)不會(huì)不正確地處理varargs形式參數(shù)。

盡管不太可取,但通過(guò)在方法聲明中添加以下內(nèi)容來(lái)抑制此類(lèi)警告也是可能的:

@SuppressWarnings({"unchecked", "varargs"})

但是,此方法不會(huì)抑制從方法的調(diào)用地點(diǎn)生成的警告,如果你不熟悉@SuppressWarnings語(yǔ)法,請(qǐng)參閱注解。

上一篇:泛型通配符使用指南 下一篇:泛型的限制

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/72954.html

相關(guān)文章

  • Java? 教程(泛型的限制)

    泛型的限制 要有效地使用Java泛型,必須考慮以下限制: 無(wú)法使用基元類(lèi)型實(shí)例化泛型類(lèi)型 無(wú)法創(chuàng)建類(lèi)型參數(shù)的實(shí)例 無(wú)法聲明類(lèi)型為類(lèi)型參數(shù)的靜態(tài)字段 無(wú)法對(duì)參數(shù)化類(lèi)型使用強(qiáng)制類(lèi)型轉(zhuǎn)換或instanceof 無(wú)法創(chuàng)建參數(shù)化類(lèi)型的數(shù)組 無(wú)法創(chuàng)建、捕獲或拋出參數(shù)化類(lèi)型的對(duì)象 無(wú)法重載將每個(gè)重載的形式參數(shù)類(lèi)型擦除為相同原始類(lèi)型的方法 無(wú)法使用基元類(lèi)型實(shí)例化泛型類(lèi)型 考慮以下參數(shù)化類(lèi)型: class P...

    Bowman_han 評(píng)論0 收藏0
  • Java 泛型總結(jié)(一):基本用法與類(lèi)型擦除

    摘要:然而中的泛型使用了類(lèi)型擦除,所以只是偽泛型??偨Y(jié)本文介紹了泛型的使用,以及類(lèi)型擦除相關(guān)的問(wèn)題。一般情況下泛型的使用比較簡(jiǎn)單,但是某些情況下,尤其是自己編寫(xiě)使用泛型的類(lèi)或者方法時(shí)要注意類(lèi)型擦除的問(wèn)題。 簡(jiǎn)介 Java 在 1.5 引入了泛型機(jī)制,泛型本質(zhì)是參數(shù)化類(lèi)型,也就是說(shuō)變量的類(lèi)型是一個(gè)參數(shù),在使用時(shí)再指定為具體類(lèi)型。泛型可以用于類(lèi)、接口、方法,通過(guò)使用泛型可以使代碼更簡(jiǎn)單、安全。然...

    Java_oldboy 評(píng)論0 收藏0
  • Java泛型:類(lèi)型擦除

    博客地址:Java泛型:類(lèi)型擦除 前情回顧 Java泛型:泛型類(lèi)、泛型接口和泛型方法 類(lèi)型擦除 代碼片段一 Class c1 = new ArrayList().getClass(); Class c2 = new ArrayList().getClass(); System.out.println(c1 == c2); /* Output true */ 顯然在平時(shí)使用中,ArrayList...

    Hanks10100 評(píng)論0 收藏0
  • 初探Java類(lèi)型擦除

    摘要:可以看到,如果我們給泛型類(lèi)制定了上限,泛型擦除之后就會(huì)被替換成類(lèi)型的上限。相應(yīng)的,泛型類(lèi)中定義的方法的類(lèi)型也是如此。參考語(yǔ)言類(lèi)型擦除下界通配符和的區(qū)別 本篇博客主要介紹了Java類(lèi)型擦除的定義,詳細(xì)的介紹了類(lèi)型擦除在Java中所出現(xiàn)的場(chǎng)景。 1. 什么是類(lèi)型擦除 為了讓你們快速的對(duì)類(lèi)型擦除有一個(gè)印象,首先舉一個(gè)很簡(jiǎn)單也很經(jīng)典的例子。 // 指定泛型為String List list1 ...

    DevTalking 評(píng)論0 收藏0
  • Java系列之泛型

    摘要:總結(jié)泛型的類(lèi)型必須是引用類(lèi)型,不能是基本類(lèi)型,泛型的個(gè)數(shù)可以有多個(gè),可以使用對(duì)創(chuàng)建對(duì)象時(shí)的泛型類(lèi)型以及方法參數(shù)類(lèi)型進(jìn)行限制,如使用關(guān)鍵字和對(duì)泛型的具體類(lèi)型進(jìn)行向下限制或向上限制,最后一點(diǎn),可以聲明泛型數(shù)組,但是不能創(chuàng)建泛型數(shù)組的實(shí)例。 自從 JDK 1.5 提供了泛型概念,泛型使得開(kāi)發(fā)者可以定義較為安全的類(lèi)型,不至于強(qiáng)制類(lèi)型轉(zhuǎn)化時(shí)出現(xiàn)類(lèi)型轉(zhuǎn)化異常,在沒(méi)有反省之前,可以通過(guò) Object...

    MadPecker 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<