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

資訊專欄INFORMATION COLUMN

Java 泛型總結(jié)(二):泛型與數(shù)組

Vultr / 2180人閱讀

摘要:總結(jié)數(shù)組與泛型的關(guān)系還是有點(diǎn)復(fù)雜的,中不允許直接創(chuàng)建泛型數(shù)組。本文分析了其中原因并且總結(jié)了一些創(chuàng)建泛型數(shù)組的方式。

簡(jiǎn)介

上一篇文章介紹了泛型的基本用法以及類型擦除的問題,現(xiàn)在來看看泛型和數(shù)組的關(guān)系。數(shù)組相比于Java 類庫(kù)中的容器類是比較特殊的,主要體現(xiàn)在三個(gè)方面:

數(shù)組創(chuàng)建后大小便固定,但效率更高

數(shù)組能追蹤它內(nèi)部保存的元素的具體類型,插入的元素類型會(huì)在編譯期得到檢查

數(shù)組可以持有原始類型 ( int,float等 ),不過有了自動(dòng)裝箱,容器類看上去也能持有原始類型了

那么當(dāng)數(shù)組遇到泛型會(huì)怎樣? 能否創(chuàng)建泛型數(shù)組呢?這是這篇文章的主要內(nèi)容。

這個(gè)系列的另外兩篇文章:

Java 泛型總結(jié)(一):基本用法與類型擦除

Java 泛型總結(jié)(三):通配符的使用

泛型數(shù)組 如何創(chuàng)建泛型數(shù)組

如果有一個(gè)類如下:

 class Generic {
    
}

如果要?jiǎng)?chuàng)建一個(gè)泛型數(shù)組,應(yīng)該是這樣: Generic ga = new Generic[]。不過行代碼會(huì)報(bào)錯(cuò),也就是說不能直接創(chuàng)建泛型數(shù)組。

那么如果要使用泛型數(shù)組怎么辦?一種方案是使用 ArrayList,比如下面的例子:

public class ListOfGenerics {
    private List array = new ArrayList();
    public void add(T item) { array.add(item); }
    public T get(int index) { return array.get(index); }
}

如何創(chuàng)建真正的泛型數(shù)組呢?我們不能直接創(chuàng)建,但可以定義泛型數(shù)組的引用。比如:

public class ArrayOfGenericReference {
    static Generic[] gia;
}

gia 是一個(gè)指向泛型數(shù)組的引用,這段代碼可以通過編譯。但是,我們并不能創(chuàng)建這個(gè)確切類型的數(shù)組,也就是不能使用 new Generic[]。具體參見下面的例子:

public class ArrayOfGeneric {
    static final int SIZE = 100;
    static Generic[] gia;
    @SuppressWarnings("unchecked")
    public static void main(String[] args) {
        // Compiles; produces ClassCastException:
        //! gia = (Generic[])new Object[SIZE];
        // Runtime type is the raw (erased) type:
        gia = (Generic[])new Generic[SIZE];
        System.out.println(gia.getClass().getSimpleName());
        gia[0] = new Generic();
        //! gia[1] = new Object(); // Compile-time error
        // Discovers type mismatch at compile time:
        //! gia[2] = new Generic();
        Generic g = gia[0];
    }
} /*輸出:
Generic[]
*///:~

數(shù)組能追蹤元素的實(shí)際類型,這個(gè)類型是在數(shù)組創(chuàng)建的時(shí)候建立的。上面被注釋掉的一行代碼: gia = (Generic[])new Object[SIZE],數(shù)組在創(chuàng)建的時(shí)候是一個(gè) Object 數(shù)組,如果轉(zhuǎn)型便會(huì)報(bào)錯(cuò)。成功創(chuàng)建泛型數(shù)組的唯一方式是創(chuàng)建一個(gè)類型擦除的數(shù)組,然后轉(zhuǎn)型,如代碼: gia = (Generic[])new Generic[SIZE]。giaClass 對(duì)象輸出的名字是 Generic[]

我個(gè)人的理解是:由于類型擦除,所以 Generic 相當(dāng)于初始類型 Generic,那么 gia = (Generic[])new Generic[SIZE] 中的轉(zhuǎn)型其實(shí)還是轉(zhuǎn)型為 Generic[],看上去像沒轉(zhuǎn),但是多了編譯器對(duì)參數(shù)的檢查和自動(dòng)轉(zhuǎn)型,向數(shù)組插入 new Object()new Generic() 均會(huì)報(bào)錯(cuò),而 gia[0] 取出給 Generic 也不需要我們手動(dòng)轉(zhuǎn)型。

使用 T[] array

上面的例子中,元素的類型是泛型類。下面看一個(gè)元素本身類型是泛型參數(shù)的例子:

public class GenericArray {
    private T[] array;
    @SuppressWarnings("unchecked")
    public GenericArray(int sz) {
        array = (T[])new Object[sz];   // 創(chuàng)建泛型數(shù)組
    }
    public void put(int index, T item) {
        array[index] = item;
    }
    public T get(int index) { return array[index]; }
    // Method that exposes the underlying representation:
    public T[] rep() { return array; }     //返回?cái)?shù)組 會(huì)報(bào)錯(cuò)
    public static void main(String[] args) {
        GenericArray gai =
        new GenericArray(10);
        // This causes a ClassCastException:
        //! Integer[] ia = gai.rep();
        // This is OK:
        Object[] oa = gai.rep();
    }
}

在上面的代碼中,泛型數(shù)組的創(chuàng)建是創(chuàng)建一個(gè) Object 數(shù)組,然后轉(zhuǎn)型為 T[]。但數(shù)組實(shí)際的類型還是 Object[]。在調(diào)用 rep()方法的時(shí)候,就報(bào) ClassCastException 異常了,因?yàn)?Object[] 無法轉(zhuǎn)型為 Integer[]。

那創(chuàng)建泛型數(shù)組的代碼 array = (T[])new Object[sz] 為什么不會(huì)報(bào)錯(cuò)呢?我的理解和前面介紹的類似,由于類型擦除,相當(dāng)于轉(zhuǎn)型為 Object[],看上去就是沒轉(zhuǎn),但是多了編譯器的參數(shù)檢查和自動(dòng)轉(zhuǎn)型。而如果把泛型參數(shù)改成 ,那么因?yàn)轭愋褪遣脸降谝粋€(gè)邊界,所以 array = (T[])new Object[sz] 中相當(dāng)于轉(zhuǎn)型為 Integer[],這應(yīng)該會(huì)報(bào)錯(cuò)。下面是實(shí)驗(yàn)的代碼:

public class GenericArray {
    private T[] array;
    @SuppressWarnings("unchecked")
    public GenericArray(int sz) {
        array = (T[])new Object[sz];   // 創(chuàng)建泛型數(shù)組
    }
    public void put(int index, T item) {
        array[index] = item;
    }
    public T get(int index) { return array[index]; }
    // Method that exposes the underlying representation:
    public T[] rep() { return array; }     //返回?cái)?shù)組 會(huì)報(bào)錯(cuò)
    public static void main(String[] args) {
        GenericArray gai =
        new GenericArray(10);
        // This causes a ClassCastException:
        //! Integer[] ia = gai.rep();
        // This is OK:
        Object[] oa = gai.rep();
    }
}

相比于原始的版本,上面的代碼只修改了第一行,把 改成了 ,那么不用調(diào)用 rep(),在創(chuàng)建泛型數(shù)組的時(shí)候就會(huì)報(bào)錯(cuò)。下面是運(yùn)行結(jié)果:

Exception in thread "main" java.lang.ClassCastException: [Ljava.lang.Object; cannot be cast to [Ljava.lang.Integer;
at GenericArray.(GenericArray.java:15)
使用 Object[] array

由于擦除,運(yùn)行期的數(shù)組類型只能是 Object[],如果我們立即把它轉(zhuǎn)型為 T[],那么在編譯期就失去了數(shù)組的實(shí)際類型,編譯器也許無法發(fā)現(xiàn)潛在的錯(cuò)誤。因此,更好的辦法是在內(nèi)部最好使用 Object[] 數(shù)組,在取出元素的時(shí)候再轉(zhuǎn)型。看下面的例子:

public class GenericArray2 {
    private Object[] array;
    public GenericArray2(int sz) {
        array = new Object[sz];
    }
    public void put(int index, T item) {
        array[index] = item;
    }
    @SuppressWarnings("unchecked")
    public T get(int index) { return (T)array[index]; }
    @SuppressWarnings("unchecked")
    public T[] rep() {
        return (T[])array; // Warning: unchecked cast
    }
    public static void main(String[] args) {
        GenericArray2 gai =
        new GenericArray2(10);
        for(int i = 0; i < 10; i ++)
        gai.put(i, i);
        for(int i = 0; i < 10; i ++)
        System.out.print(gai.get(i) + " ");
        System.out.println();
        try {
            Integer[] ia = gai.rep();
        } catch(Exception e) { System.out.println(e); }
    }
} /* Output: (Sample)
0 1 2 3 4 5 6 7 8 9
java.lang.ClassCastException: [Ljava.lang.Object; cannot be cast to [Ljava.lang.Integer;
*///:~

現(xiàn)在內(nèi)部數(shù)組的呈現(xiàn)不是 T[] 而是 Object[],當(dāng) get() 被調(diào)用的時(shí)候數(shù)組的元素被轉(zhuǎn)型為 T,這正是元素的實(shí)際類型。不過調(diào)用 rep() 還是會(huì)報(bào)錯(cuò), 因?yàn)閿?shù)組的實(shí)際類型依然是Object[],終究不能轉(zhuǎn)換為其它類型。使用 Object[] 代替 T[] 的好處是讓我們不會(huì)忘記數(shù)組運(yùn)行期的實(shí)際類型,以至于不小心引入錯(cuò)誤。

使用類型標(biāo)識(shí)

其實(shí)使用 Class 對(duì)象作為類型標(biāo)識(shí)是更好的設(shè)計(jì):

public class GenericArrayWithTypeToken {
    private T[] array;
    @SuppressWarnings("unchecked")
    public GenericArrayWithTypeToken(Class type, int sz) {
        array = (T[])Array.newInstance(type, sz);
    }
    public void put(int index, T item) {
        array[index] = item;
    }
    public T get(int index) { return array[index]; }
    // Expose the underlying representation:
    public T[] rep() { return array; }
    public static void main(String[] args) {
        GenericArrayWithTypeToken gai =
        new GenericArrayWithTypeToken(
        Integer.class, 10);
        // This now works:
        Integer[] ia = gai.rep();
    }
}

在構(gòu)造器中傳入了 Class 對(duì)象,通過 Array.newInstance(type, sz) 創(chuàng)建一個(gè)數(shù)組,這個(gè)方法會(huì)用參數(shù)中的 Class 對(duì)象作為數(shù)組元素的組件類型。這樣創(chuàng)建出的數(shù)組的元素類型便不再是 Object,而是 T。這個(gè)方法返回 Object 對(duì)象,需要把它轉(zhuǎn)型為數(shù)組。不過其他操作都不需要轉(zhuǎn)型了,包括 rep() 方法,因?yàn)閿?shù)組的實(shí)際類型與 T[] 是一致的。這是比較推薦的創(chuàng)建泛型數(shù)組的方法。

總結(jié)

數(shù)組與泛型的關(guān)系還是有點(diǎn)復(fù)雜的,Java 中不允許直接創(chuàng)建泛型數(shù)組。本文分析了其中原因并且總結(jié)了一些創(chuàng)建泛型數(shù)組的方式。其中有部分個(gè)人的理解,如果錯(cuò)誤希望大家指正。下一篇會(huì)總結(jié)通配符的使用,有興趣的讀者可進(jìn)入下一篇:Java 泛型總結(jié)(三):通配符的使用。

參考

Java 編程思想

如果我的文章對(duì)您有幫助,不妨點(diǎn)個(gè)贊支持一下(^_^)

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

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

相關(guān)文章

  • Java 泛型總結(jié)(一):基本用法與類型擦除

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

    Java_oldboy 評(píng)論0 收藏0
  • Java 泛型總結(jié)(三):通配符的使用

    簡(jiǎn)介 前兩篇文章介紹了泛型的基本用法、類型擦除以及泛型數(shù)組。在泛型的使用中,還有個(gè)重要的東西叫通配符,本文介紹通配符的使用。 這個(gè)系列的另外兩篇文章: Java 泛型總結(jié)(一):基本用法與類型擦除 Java 泛型總結(jié)(二):泛型與數(shù)組 數(shù)組的協(xié)變 在了解通配符之前,先來了解一下數(shù)組。Java 中的數(shù)組是協(xié)變的,什么意思?看下面的例子: class Fruit {} class Apple ex...

    itvincent 評(píng)論0 收藏0
  • JAVA語(yǔ)法糖和語(yǔ)法糖編譯

    摘要:提供給了用戶大量的語(yǔ)法糖,比如泛型自動(dòng)裝箱拆箱循環(huán)變長(zhǎng)參數(shù)內(nèi)部類枚舉類斷言新特性方法引用等解語(yǔ)法糖語(yǔ)法糖的存在主要是方便開發(fā)人員使用。 首先,部分總結(jié)文字引用 簡(jiǎn)書作者:Eric新之助 。鏈接:https://www.jianshu.com/p/4de08deb6ba4 已獲得授權(quán) showImg(https://segmentfault.com/img/bVbfuX9?w=646&...

    weakish 評(píng)論0 收藏0
  • Java? 教程(類型推斷)

    類型推斷 類型推斷是Java編譯器查看每個(gè)方法調(diào)用和相應(yīng)聲明的能力,以確定使調(diào)用適用的類型參數(shù),推理算法確定參數(shù)的類型,如果可用,還確定分配或返回結(jié)果的類型,最后,推理算法嘗試查找適用于所有參數(shù)的最具體類型。 為了說明最后一點(diǎn),在下面的示例中,推斷確定傳遞給pick方法的第二個(gè)參數(shù)是Serializable類型: static T pick(T a1, T a2) { return a2; } ...

    JerryC 評(píng)論0 收藏0
  • Java 虛擬機(jī)總結(jié)給面試的你(中)

    摘要:驗(yàn)證過程驗(yàn)證過程的目的是為了確保文件的字節(jié)流中包含的信息符合當(dāng)前虛擬機(jī)的要求,并且不會(huì)危害虛擬機(jī)自身的安全。二虛擬機(jī)字節(jié)碼執(zhí)行引擎虛擬機(jī)的執(zhí)行引擎自行實(shí)現(xiàn),可以自行制定指令集與執(zhí)行引擎的結(jié)構(gòu)體系。 本篇博客主要針對(duì)Java虛擬機(jī)的類加載機(jī)制,虛擬機(jī)字節(jié)碼執(zhí)行引擎,早期編譯優(yōu)化進(jìn)行總結(jié),其余部分總結(jié)請(qǐng)點(diǎn)擊Java虛擬總結(jié)上篇 。 一.虛擬機(jī)類加載機(jī)制 概述 虛擬機(jī)把描述類的數(shù)據(jù)從Clas...

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

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

0條評(píng)論

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