国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

Java泛型和類型擦除

el09xccxy / 2666人閱讀

摘要:對(duì)每個(gè)泛型類只生成唯一的一份目標(biāo)代碼該泛型類的所有實(shí)例都映射到這份目標(biāo)代碼上,在需要的時(shí)候執(zhí)行類型檢查和類型轉(zhuǎn)換。參考文章的模板是典型的實(shí)現(xiàn),而泛型則是實(shí)現(xiàn),將多種泛型類形實(shí)例映射到唯一的字節(jié)碼表示是通過類型擦除實(shí)現(xiàn)的。

一 前言:初識(shí)泛型

廢話不說,先來看一段代碼:

public class Holder {
    private Object data;
    
    public Holder(Object data ){
        this.data = data;
    }

    public void setData(Object data) {
        this.data = data;
    }

    public Object getData() {
        return data;
    }
    
    public static void main(String[] args){
        Holder holder = new Holder(new SomeNode());
        SomeNode someNode = holder.getData();
        
    }
}

class SomeNode{}

Holder類是一個(gè)容器,它的作用是用來保存其他類的,這里我們用它來保存SomeNode類,隨后把它取出來,編譯運(yùn)行,結(jié)果如下:

Error:(21, 43) java: incompatible types
  required: SomeNode
  found:    java.lang.Object

意思是,需要的是SomeNode,取出來的卻是Object,如此看來,如果我想保存SomeNode類,就只能把data聲明為SomeNode:

private SomeNode data;

這就意味著我們需要為每一個(gè)類創(chuàng)造一個(gè)Holder,這肯定是不行的,于是泛型的作用來了,泛型,可以理解為任何類型,意思是我可以聲明一個(gè)可以容納任何類型的容器:

public class Holder {
    private T data;

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

    public void setData(T data) {
        this.data = data;
    }

    public T getData() {
        return data;
    }

    public static void main(String[] args){
        Holder holder = new Holder(new SomeNode());
        SomeNode someNode = holder.getData();

    }
}

class SomeNode{}

注意寫法,在類聲明后面加個(gè)就行了,你也可以加,只是一個(gè)占位符,形參而已。然后我們?cè)侔阉〕鰜恚?/p>

Process finished with exit code 0

程序沒有報(bào)錯(cuò),如果這時(shí)候我們使用holder的set()方法去插入設(shè)置一些非SomeNode類型的值,代碼如下:

 public static void main(String[] args){
        Holder holder = new Holder(new SomeNode());
        SomeNode someNode = holder.getData();
        holder.setData("AAAA");

    }

看結(jié)果:

Error:(22, 15) java: method setData in class Holder cannot be applied to given types;
  required: SomeNode
  found: java.lang.String
  reason: actual argument java.lang.String cannot be converted to SomeNode by method invocation conversion

泛型機(jī)制就自動(dòng)為我們報(bào)錯(cuò),很方便。

二 泛型類:元組(Tuple),返回多個(gè)對(duì)象

熟悉python的同學(xué)都知道元組的概念,它是一個(gè)只讀列表,在返回多個(gè)結(jié)果時(shí)是很有用的,我們利用泛型特性來創(chuàng)造一個(gè)包含兩個(gè)對(duì)象的元組:

public class Tuple {
    public static void main(String[] args){
        TwoTuple t = new TwoTuple("Monkey",12);
        System.out.println(t.toString());
    }
}

class TwoTuple{
    final A first;
    final B second;

    public TwoTuple(A a,B b){
        first = a;
        second = b;
    }

    public String toString(){
        return "("+first+","+second+")";
    }
}

來看結(jié)果:

(Monkey,12)

是不是很方便:)如果想要一個(gè)長(zhǎng)度為3的元組可以這么寫:

public class Tuple {
    public static void main(String[] args){
        ThreeTuple t = new ThreeTuple("Dog",12,true);
        System.out.println(t.toString());
    }
}

class TwoTuple{
    final A first;
    final B second;

    public TwoTuple(A a,B b){
        first = a;
        second = b;
    }

    public String toString(){
        return "("+first+","+second+")";
    }
}

class ThreeTuple extends TwoTuple{
    final C three;

    public ThreeTuple(A a,B b,C c){
        super(a,b);
        three = c;
    }

    public String toString(){
        return "("+first+","+second+","+three+")";
    }
}

結(jié)果如下:

(Dog,12,true)
三 泛型接口

泛型接口的定義和泛型類的定義類似,我們來定義一個(gè)生成器接口:

public interface Generator {
    T next();
}

接著我們實(shí)現(xiàn)這個(gè)接口,來生成斐波拉契數(shù):

public class Fib implements Generator {
    private int count = 0;

    @Override
    public Integer next() {
        return fib(count++);
    }

    private int fib(int n){
        if (n<2)
            return 1;
        else
            return fib(n-2) + fib(n-1);
    }

    public static void main(String[] args){
        Fib f = new Fib();
        for (int i=0;i<100;i++){
            System.out.println(f.next());
        }
    }

}
四 泛型方法

比起泛型類,我們更推薦去使用泛型方法,泛型方法定義起來也很簡(jiǎn)單,我們只需將泛型參數(shù)放在返回類型前面即可:

public class GenericMethods {
    public  void f(T x){
        System.out.println(x.getClass().getName());
    }

    public static void main(String[] args){
        GenericMethods g = new GenericMethods();
        g.f("Hello");
        g.f(100);
        g.f(true);
    }
}

這里我們定義了一個(gè)泛型方法f(),并使用getClass獲取類的相關(guān)信息(關(guān)于Class對(duì)象的知識(shí)點(diǎn)這里),來看結(jié)果:

java.lang.String
java.lang.Integer
java.lang.Boolean

這里還要注意一下Varargs(變長(zhǎng)參數(shù))機(jī)制和泛型的結(jié)合:

public class GenericVarargs {
    public static  List makeList(T...args){
        List list = new ArrayList();
        for (T item : args){
            list.add(item);
        }

        return list;
    }

    public static void main(String[] args){
        List list = makeList("A","B","C","D");
        System.out.println(list);
    }
}

結(jié)果如下:

[A, B, C, D]
六 類型擦除

在認(rèn)識(shí)類型擦除之前,我們首先要明白編譯器對(duì)泛型的處理有兩種方式:
1.Code specialization
在實(shí)例化一個(gè)泛型類或者泛型方法是都生成一份新的字節(jié)碼,比如對(duì)于List,List,List產(chǎn)生三份不同的字節(jié)碼。
2.Code sharing
對(duì)每個(gè)泛型類只生成唯一的一份目標(biāo)代碼;該泛型類的所有實(shí)例都映射到這份目標(biāo)代碼上,在需要的時(shí)候執(zhí)行類型檢查和類型轉(zhuǎn)換。參考文章
C++的模板是典型的Code specialization實(shí)現(xiàn),而Java泛型則是Code sharing實(shí)現(xiàn),將多種泛型類形實(shí)例映射到唯一的字節(jié)碼表示是通過類型擦除(type erasue)實(shí)現(xiàn)的。對(duì)擦除更通俗的理解就是:編譯器生成的bytecode是不包涵泛型信息的。我們看下面的代碼:

public class ErasedType {
    public static void main(String[] args){
        Class c1 = new ArrayList().getClass();
        Class c2 = new ArrayList().getClass();

        System.out.println(c1 == c2);
    }
}

結(jié)果如下:

true

也就是說我們?cè)趯?shí)例化ArrayList和實(shí)例化ArrayList時(shí)是共享一份目標(biāo)代碼的,泛型類類型信息在編譯的過程中被擦除了。對(duì)于JVM來說,它只看到一份ArrayList(原始類型)而已。我們還可以從反射的角度來理解類型擦除:

public class ErasedType {
    public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        List list = new ArrayList();
        list.add("ABC");
        list.getClass().getMethod("add",Object.class).invoke(list,123);
        System.out.println(list);
    }

}

看結(jié)果:

[ABC, 123]

我們很順利的把Integer型的123插入到了String的List里:)

七 后記

由于類型擦除的存在,我們往往會(huì)在使用泛型特性的時(shí)候遇到一些詭異的問題,由于篇幅原因,這里不展開了:)我將在另外一篇文章中集中的總結(jié)一下這方面的問題。

我的微信號(hào)是aristark,歡迎交流指正!

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

轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/65687.html

相關(guān)文章

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

    摘要:總結(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ù)組可以持...

    Vultr 評(píng)論0 收藏0
  • Java語(yǔ)法糖的編譯結(jié)果分析(一)

    摘要:操作對(duì)應(yīng)字節(jié)碼中的個(gè)字節(jié)我們可以看到最關(guān)鍵的操作其實(shí)就是調(diào)用的其實(shí)是類的方法,此方法的入?yún)㈩愋褪牵祷刂殿愋褪牵g過來就是類的方法,執(zhí)行完后將獲得的結(jié)果做了,檢查返回的對(duì)象類型是否是。 語(yǔ)法糖(Syntactic Sugar)的出現(xiàn)是為了降低我們編寫某些代碼時(shí)陷入的重復(fù)或繁瑣,這使得我們使用語(yǔ)法糖后可以寫出簡(jiǎn)明而優(yōu)雅的代碼。在Java中不加工的語(yǔ)法糖代碼運(yùn)行時(shí)可不會(huì)被虛擬機(jī)接受,因此...

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

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

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

0條評(píng)論

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