Java 泛型進(jìn)階

擦除


在泛型代碼內(nèi)部颂跨,無法獲得任何有關(guān)泛型參數(shù)類型的信息笆豁。

例子1:

//這個例子表明編譯過程中并沒有根據(jù)參數(shù)生成新的類型
public class Main2 {
    public static void main(String[] args) {
        Class c1 = new ArrayList<Integer>().getClass();
        Class c2 = new ArrayList<String>().getClass();
        System.out.print(c1 == c2);
    }
}
/* output
true
*/

List<String> 中添加 Integer 將不會通過編譯苗傅,但是List<Sring>List<Integer>在運(yùn)行時的確是同一種類型壶栋。

例子2:

//例子, 這個例子表明類的參數(shù)類型跟傳進(jìn)去的類型沒有關(guān)系埠啃,泛型參數(shù)只是`占位符`
public class Table {
}
public class Room {
}
public class House<Q> {
}
public class Particle<POSITION, MOMENTUM> {
}
public class Main {
    public static void main(String[] args) {
        List<Table> tableList = new ArrayList<Table>();
        Map<Room, Table> maps = new HashMap<Room, Table>();
        House<Room> house = new House<Room>();
        Particle<Long, Double> particle = new Particle<Long, Double>();
        System.out.println(Arrays.toString(tableList.getClass().getTypeParameters()));
        System.out.println(Arrays.toString(maps.getClass().getTypeParameters()));
        System.out.println(Arrays.toString(house.getClass().getTypeParameters()));
        System.out.println(Arrays.toString(particle.getClass().getTypeParameters()));
    }
}
/** output
[E]
[K, V]
[Q]
[POSITION, MOMENTUM]
*/

我們在運(yùn)行期試圖獲取一個已經(jīng)聲明的類的類型參數(shù)鹏控,發(fā)現(xiàn)這些參數(shù)依舊是‘形參’庶近,并沒有隨聲明改變富蓄。也就是說在運(yùn)行期胖缤,我們是拿不到已經(jīng)聲明的類型的任何信息尚镰。

編譯器會雖然在編譯過程中移除參數(shù)的類型信息,但是會保證類或方法內(nèi)部參數(shù)類型的一致性哪廓。

例子:

List<String> stringList=new ArrayList<String>();
//可以通過編譯
stringList.add("wakaka");
//編譯不通過
//stringList.add(new Integer(0));

//List.java
public interface List<E> extends Collection<E> {
//...
boolean add(E e);
//...
}

List的參數(shù)類型是E狗唉,add方法的參數(shù)類型也是E,他們在類的內(nèi)部是一致的涡真,所以添加Integer類型的對象到stringList違反了內(nèi)部類型一致分俯,不能通過編譯。

重用 extends 關(guān)鍵字哆料。通過它能給與參數(shù)類型添加一個邊界缸剪。

泛型參數(shù)將會被擦除到它的第一個邊界(邊界可以有多個)。編譯器事實上會把類型參數(shù)替換為它的第一個邊界的類型东亦。如果沒有指明邊界杏节,那么類型參數(shù)將被擦除到Object。下面的例子中,可以把泛型參數(shù)T當(dāng)作HasF類型來使用奋渔。

例子:

/** * Created by yxf on 16-5-28. */
// HasF.java
public interface HasF {
    void f();
}

//Manipulator.java
public class Manipulator<T extends HasF> {
    T obj;
    public T getObj() {
        return obj;
    }
    public void setObj(T obj) {
        this.obj = obj;
    }
}

extend關(guān)鍵字后后面的類型信息決定了泛型參數(shù)能保留的信息镊逝。

Java中擦除的基本原理

剛看到這里可能有些困惑,一個泛型類型沒有保留具體聲明的類型的信息卒稳,那它是怎么工作的呢蹋半?在把《Java編程思想》書中這里的邊界與上文的邊界區(qū)分開來之后,終于想通了充坑。Java的泛型類的確只有一份字節(jié)碼减江,但是在使用泛型類的時候編譯器做了特殊的處理。

這里根據(jù)作者的思路捻爷,自己動手寫了兩個類SimpleHolderGenericHolder辈灼,然后編譯拿到兩個類的字節(jié)碼,直接貼在這里:

// SimpleHolder.java
public class SimpleHolder {
    private Object obj;
    public Object getObj() {
        return obj;
    }
    public void setObj(Object obj) {
        this.obj = obj;
    }
    public static void main(String[] args) {
        SimpleHolder holder = new SimpleHolder();
        holder.setObj("Item");
        String s = (String) holder.getObj();
    }
}
// SimpleHolder.class
public class SimpleHolder {
  public SimpleHolder();
    Code:
       0: aload_0       
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return        

  public java.lang.Object getObj();
    Code:
       0: aload_0       
       1: getfield      #2                  // Field obj:Ljava/lang/Object;
       4: areturn       

  public void setObj(java.lang.Object);
    Code:
       0: aload_0       
       1: aload_1       
       2: putfield      #2                  // Field obj:Ljava/lang/Object;
       5: return        

  public static void main(java.lang.String[]);
    Code:
       0: new           #3                  // class SimpleHolder
       3: dup           
       4: invokespecial #4                  // Method "<init>":()V
       7: astore_1      
       8: aload_1       
       9: ldc           #5                  // String Item
      11: invokevirtual #6                  // Method setObj:(Ljava/lang/Object;)V
      14: aload_1       
      15: invokevirtual #7                  // Method getObj:()Ljava/lang/Object;
      18: checkcast     #8                  // class java/lang/String
      21: astore_2      
      22: return        
}
//GenericHolder.java
public class GenericHolder<T> {
    T obj;
    public T getObj() {
        return obj;
    }
    public void setObj(T obj) {
        this.obj = obj;
    }
    public static void main(String[] args) {
        GenericHolder<String> holder = new GenericHolder<>();
        holder.setObj("Item");
        String s = holder.getObj();
    }
}

//GenericHolder.class
public class GenericHolder<T> {
  T obj;

  public GenericHolder();
    Code:
       0: aload_0       
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return        

  public T getObj();
    Code:
       0: aload_0       
       1: getfield      #2                  // Field obj:Ljava/lang/Object;
       4: areturn       

  public void setObj(T);
    Code:
       0: aload_0       
       1: aload_1       
       2: putfield      #2                  // Field obj:Ljava/lang/Object;
       5: return        

  public static void main(java.lang.String[]);
    Code:
       0: new           #3                  // class GenericHolder
       3: dup           
       4: invokespecial #4                  // Method "<init>":()V
       7: astore_1      
       8: aload_1       
       9: ldc           #5                  // String Item
      11: invokevirtual #6                  // Method setObj:(Ljava/lang/Object;)V
      14: aload_1       
      15: invokevirtual #7                  // Method getObj:()Ljava/lang/Object;
      18: checkcast     #8                  // class java/lang/String
      21: astore_2      
      22: return        
}

經(jīng)過一番比較之后也榄,發(fā)現(xiàn)兩分源碼雖然不同巡莹,但是對應(yīng)的字節(jié)碼邏輯部分確是完全相同的。

在編譯過程中甜紫,類型變量的信息是能拿到的降宅。所以,set方法在編譯器可以做類型檢查囚霸,非法類型不能通過編譯腰根。但是對于get方法,由于擦除機(jī)制拓型,運(yùn)行時的實際引用類型為Object類型额嘿。為了‘還原’返回結(jié)果的類型,編譯器在get之后添加了類型轉(zhuǎn)換劣挫。所以册养,在GenericHolder.class文件main方法主體第18行有一處類型轉(zhuǎn)換的邏輯。它是編譯器自動幫我們加進(jìn)去的压固。

所以在泛型類對象讀取和寫入的位置為我們做了處理球拦,為代碼添加約束。

擦除的缺陷

泛型類型不能顯式地運(yùn)用在運(yùn)行時類型的操作當(dāng)中帐我,例如:轉(zhuǎn)型坎炼、instanceofnew。因為在運(yùn)行時焚刚,所有參數(shù)的類型信息都丟失了。

public class Erased<T> {
    private final int SIZE = 100;
    public static void f(Object arg) {
        //編譯不通過
        if (arg instanceof T) {
        }
        //編譯不通過
        T var = new T();
        //編譯不通過
        T[] array = new T[SIZE];
        //編譯不通過
        T[] array = (T) new Object[SIZE];
    }
}

擦除的補(bǔ)償

1. 類型判斷問題

例子:

class Building {}
class House extends Building {}
public class ClassTypeCapture<T> {
    Class<T> kind;
    public ClassTypeCapture(Class<T> kind) {
        this.kind = kind;
    }
    public boolean f(Object arg) {
        return kind.isInstance(arg);
    }
    public static void main(String[] args) {
        ClassTypeCapture<Building> ctt1 = new ClassTypeCapture<Building>(Building.class);
        System.out.println(ctt1.f(new Building()));
        System.out.println(ctt1.f(new House()));
        ClassTypeCapture<House> ctt2 = new ClassTypeCapture<House>(House.class);
        System.out.println(ctt2.f(new Building()));
        System.out.print(ctt2.f(new House()));
    }
}
//output
//true
//true
//false
//true

泛型參數(shù)的類型無法用instanceof關(guān)鍵字來做判斷扇调。所以我們使用類類型來構(gòu)造一個類型判斷器矿咕,判斷一個實例是否為特定的類型。

2. 創(chuàng)建類型實例

Erased.java中不能new T()的原因有兩個,一是因為擦除碳柱,不能確定類型捡絮;而是無法確定T是否包含無參構(gòu)造函數(shù)。

為了避免這兩個問題莲镣,我們使用顯式的工廠模式:

例子:

interface IFactory<T> {
    T create();
}

class Foo2<T> {
    private T x;

    public <F extends IFactory<T>> Foo2(F factory) {
        x = factory.create();
    }
}

class IntegerFactory implements IFactory<Integer> {
    @Override
    public Integer create() {
        return new Integer(0);
    }
}

class Widget {
    public static class Factory implements IFactory<Widget> {
        @Override
        public Widget create() {
            return new Widget();
        }
    }
}

public class FactoryConstraint {
    public static void main(String[] args) {
        new Foo2<Integer>(new IntegerFactory());
        new Foo2<Widget>(new Widget.Factory());
    }
}

通過特定的工廠類實現(xiàn)特定的類型能夠解決實例化類型參數(shù)的需求福稳。

3. 創(chuàng)建泛型數(shù)組

一般不建議創(chuàng)建泛型數(shù)組。盡量使用ArrayList來代替泛型數(shù)組瑞侮。但是在這里還是給出一種創(chuàng)建泛型數(shù)組的方法的圆。

public class GenericArrayWithTypeToken<T> {
    private T[] array;

    @SuppressWarnings("unchecked")
    public GenericArrayWithTypeToken(Class<T> type, int sz) {
        array = (T[]) Array.newInstance(type, sz);
    }

    public void put(int index, T item) {
        array[index] = item;
    }

    public T[] rep() {
        return array;
    }

    public static void main(String[] args) {
        GenericArrayWithTypeToken<Integer> gai = new GenericArrayWithTypeToken<Integer>(Integer.class, 10);
        Integer[] ia = gai.rep();
    }
}

這里我們使用的還是傳參數(shù)類型,利用類型的newInstance方法創(chuàng)建實例的方式半火。

邊界


這里Java重用了 extend關(guān)鍵字越妈。邊界可以將類型參數(shù)的范圍限制到一個子集當(dāng)中。

interface HasColor {
    Color getColor();
}

class Colored<T extends HasColor> {
    T item;

    public Colored(T item) {
        this.item = item;
    }

    public T getItem() {
        return item;
    }

    public Color color() {
        return item.getColor();
    }
}

class Dimension {
    public int x, y, z;
}

class ColoredDemension<T extends HasColor & Dimension> {
    T item;

    public ColoredDemension(T item) {
        this.item = item;
    }

    public T getItem() {
        return item;
    }

    Color color() {
        return item.getColor();
    }

    int getX() {
        return item.x;
    }

    int getY() {
        return item.y;
    }

    int getZ() {
        return item.z;
    }

}

interface Weight {
    int weight();
}

class Solid<T extends Dimension & HasColor & Weight> {
    T item;

    public Solid(T item) {
        this.item = item;
    }

    public T getItem() {
        return item;
    }

    Color color() {
        return item.getColor();
    }

    int getX() {
        return item.x;
    }

    int getY() {
        return item.y;
    }

    int getZ() {
        return item.z;
    }

    int weight() {
        return item.weight();
    }
}

class Bounded extends Dimension implements HasColor, Weight {
    @Override
    public Color getColor() {
        return null;
    }

    @Override
    public int weight() {
        return 0;
    }
}

public class BasicBound {
    public static void main(String[] args) {
        Solid<Bounded> solid = new Solid<Bounded>(new Bounded());
        solid.color();
        solid.weight();
        solid.getZ();
    }
}

extends關(guān)鍵字聲明中钮糖,有兩個要注意的地方:

  1. 類必須要寫在接口之前梅掠;
  2. 只能設(shè)置一個類做邊界,其它均為接口店归。

通配符


協(xié)變:

public class Holder<T> {
    private T value;

    public Holder(T apple) {
    }

    public T getValue() {
        return value;
    }

    public void setValue(T value) {
        this.value = value;
    }

    @Override
    public boolean equals(Object o) {
        return value != null && value.equals(o);
    }

    public static void main(String[] args) {
        Holder<Apple> appleHolder = new Holder<Apple>(new Apple());
        Apple d = new Apple();
        appleHolder.setValue(d);

        // 不能自動協(xié)變
        // Holder<Fruit> fruitHolder=appleHolder;

        // 借助 ? 通配符和 extends 關(guān)鍵字可以實現(xiàn)協(xié)變
        Holder<? extends Fruit> fruitHolder = appleHolder;

        // 返回一個Fruit消痛,因為添加邊界之后返回的對象是 ? extends Fruit,
        // 可以把它轉(zhuǎn)型為Apple且叁,但是在不知道具體類型的時候存在風(fēng)險
        d = (Apple) fruitHolder.getValue();

        //Fruit以及Fruit的父類,就不需要轉(zhuǎn)型
        Fruit fruit = fruitHolder.getValue();
        Object obj = fruitHolder.getValue();

        try {
            Orange c = (Orange) fruitHolder.getValue();
        } catch (Exception e) {
            System.out.print(e);
        }

        // 編譯不通過稠歉,因為編譯階段根本不知道子類型到底是什么類型
        //        fruitHolder.setValue(new Apple());
        //        fruitHolder.setValue(new Orange());

        //這里是可以的因為equals方法接受的是Object作為參數(shù)掰担,并不是 ? extends Fruit
        System.out.print(fruitHolder.equals(d));
    }
}

在Java中父類型可以持有子類型。如果一個父類的容器可以持有子類的容器怒炸,那么我們就可以稱為發(fā)生了協(xié)變带饱。在java中,數(shù)組是自帶協(xié)變的阅羹,但是泛型的容器沒有自帶協(xié)變勺疼。我們可以根據(jù)利用邊界和通配符?來實現(xiàn)近似的協(xié)變。

Holder<? extends Fruit>就是一種協(xié)變的寫法捏鱼。它表示一個列表执庐,列表持有的類型是Fruit或其子類。

這個Holder<? extends Fruit>運(yùn)行時持有的類型是未知的导梆,我們只知道它一定是Fruit的子類轨淌。正因為如此迂烁,所以我們無法向這個holder中放入任何類型的對象,Object類型的對象也不可以递鹉。但是盟步,調(diào)用它的返回方法卻是可以的。因為邊界明確定義了它是Fruit類型的子類躏结。

逆變:

package wildcard;

import java.util.ArrayList;
import java.util.List;

public class GenericWriting {
    static <T> void writeExact(List<T> list, T item) {
        list.add(item);
    }

    static List<Apple> apples = new ArrayList<Apple>();
    static List<Fruit> fruits = new ArrayList<Fruit>();

    static void f1() {
        writeExact(apples, new Apple());
        //this cannot be compile,said in Thinking in Java
        writeExact(fruits, new Apple());
    }

    static <T> void writeWithWildcard(List<? super T> list, T item) {
        list.add(item);
    }

    static void f2() {
        writeWithWildcard(apples, new Apple());
        writeWithWildcard(fruits, new Apple());
    }

    static <T> readWithWildcard(List<? super T> list, int index) {
        //Compile Error, required T but found Object
        return list.get(index);
    }
    public static void main(String[] args) {
        f1();
        f2();
    }
}

如果一個類的父類型容器可以持有該類的子類型的容器却盘,我們稱這種關(guān)系為逆變。聲明方式List<? super Integer>, List<? super T> list媳拴。

不能給泛型參數(shù)給出一個超類型邊界黄橘;即不能聲明List<T super MyClass>

上面的例子中禀挫,writeExact(fruits,new Apple());在《Java編程思想》中說是不能通過編譯的旬陡,但我試了一下,在Java1.6语婴,Java1.7中是可以編譯的描孟。不知道是不是編譯器比1.5版本升級了。

由于給出了參數(shù)類型的‘下界’砰左,所以我們可以在列表中添加數(shù)據(jù)而不會出現(xiàn)類型錯誤匿醒。但是使用get方法獲取返回類型的時候要注意,由于聲明的類型區(qū)間是Object到T具有繼承關(guān)系的類缠导。所以返回的類型為了確保沒有問題廉羔,都是以O(shè)bject類型返回回來的。比如過例子中list.get(index)的返回類型就是Object僻造。

無界通配符

無界通配符<?> 意味著可以使用任何對象憋他,因此使用它類似于使用原生類型。但它是有作用的髓削,原生類型可以持有任何類型竹挡,而無界通配符修飾的容器持有的是某種具體的類型。舉個例子立膛,在List<?>類型的引用中揪罕,不能向其中添加Object, 而List類型的引用就可以添加Object類型的變量。

一些需要注意的問題


1. 任何基本類型都不能作為類型參數(shù)

2. 實現(xiàn)參數(shù)化接口

例子:

interface Payable<T>{}
class Employee implements Payable<Employee> {}
//Compile Error
class Hourly extends Employee implements Payable<Hourly> {}

因為擦除的原因宝泵,Payable<Employee>Payable<Hourly>簡化為相同的Payable<Object>好啰,例子中的代碼意味著重復(fù)兩次實現(xiàn)相同的接口。但他們的參數(shù)類型卻是不相同的儿奶。

3. 轉(zhuǎn)型和警告

使用帶有泛型類型參數(shù)的轉(zhuǎn)型或者instanceof不會有任何效果框往。因為他們在運(yùn)行時都會被擦除到上邊界上。所以轉(zhuǎn)型的時候用的類型實際上是上邊解對應(yīng)的類型闯捎。

4. 重載

//Compile Error. 編譯不能通過
public class UseList<W,T>{
    void f(List<T> v){}
    void f(List<W> v){}
}

由于擦除的原因椰弊,重載方法將產(chǎn)生相同的類型簽名嘁酿。避免這種問題的方法就是換個方法名。

5. 基類劫持接口

例子:

public class ComparablePet implements Comparable<ComparablePet>{
    public int compareTo(ComparablePet arg) {return 0;}
}
class Cat extends ComparablePet implements Comparable<Cat>{
    // Error: Comparable connot be inherited with
    // different arguments: <Cat> and <ComparablePet>
    public int compareTo(Cat arg);
}

父類中我們?yōu)?code>Comparable確定了ComparablePet參數(shù)男应,那么其它任何類型都不能再與ComparablePet之外的對象再比較。子類中不能對同一個接口用不同的參數(shù)實現(xiàn)兩次娱仔。這有點(diǎn)類似于第四點(diǎn)中的重載沐飘。
但是我們可以在子類中覆寫父類中的方法。

關(guān)于泛型問題就先了解這么多牲迫,有什么不對的地方還請大家指正耐朴。也歡迎小伙伴們一起交流。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末盹憎,一起剝皮案震驚了整個濱河市筛峭,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌陪每,老刑警劉巖影晓,帶你破解...
    沈念sama閱讀 219,039評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異檩禾,居然都是意外死亡挂签,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,426評論 3 395
  • 文/潘曉璐 我一進(jìn)店門盼产,熙熙樓的掌柜王于貴愁眉苦臉地迎上來饵婆,“玉大人,你說我怎么就攤上這事戏售∏群耍” “怎么了?”我有些...
    開封第一講書人閱讀 165,417評論 0 356
  • 文/不壞的土叔 我叫張陵灌灾,是天一觀的道長搓译。 經(jīng)常有香客問我,道長紧卒,這世上最難降的妖魔是什么侥衬? 我笑而不...
    開封第一講書人閱讀 58,868評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮跑芳,結(jié)果婚禮上轴总,老公的妹妹穿的比我還像新娘。我一直安慰自己博个,他們只是感情好怀樟,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,892評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著盆佣,像睡著了一般往堡。 火紅的嫁衣襯著肌膚如雪械荷。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,692評論 1 305
  • 那天虑灰,我揣著相機(jī)與錄音吨瞎,去河邊找鬼。 笑死穆咐,一個胖子當(dāng)著我的面吹牛颤诀,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播对湃,決...
    沈念sama閱讀 40,416評論 3 419
  • 文/蒼蘭香墨 我猛地睜開眼崖叫,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了拍柒?” 一聲冷哼從身側(cè)響起心傀,我...
    開封第一講書人閱讀 39,326評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎拆讯,沒想到半個月后脂男,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,782評論 1 316
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡种呐,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,957評論 3 337
  • 正文 我和宋清朗相戀三年疆液,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片陕贮。...
    茶點(diǎn)故事閱讀 40,102評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡堕油,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出肮之,到底是詐尸還是另有隱情掉缺,我是刑警寧澤,帶...
    沈念sama閱讀 35,790評論 5 346
  • 正文 年R本政府宣布戈擒,位于F島的核電站眶明,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏筐高。R本人自食惡果不足惜搜囱,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,442評論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望柑土。 院中可真熱鬧蜀肘,春花似錦、人聲如沸稽屏。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,996評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽狐榔。三九已至坛增,卻和暖如春获雕,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背收捣。 一陣腳步聲響...
    開封第一講書人閱讀 33,113評論 1 272
  • 我被黑心中介騙來泰國打工届案, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人罢艾。 一個月前我還...
    沈念sama閱讀 48,332評論 3 373
  • 正文 我出身青樓萝玷,卻偏偏與公主長得像,于是被迫代替她去往敵國和親昆婿。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,044評論 2 355

推薦閱讀更多精彩內(nèi)容

  • 開發(fā)人員在使用泛型的時候蜓斧,很容易根據(jù)自己的直覺而犯一些錯誤仓蛆。比如一個方法如果接收List作為形式參數(shù),那么如果嘗試...
    時待吾閱讀 1,055評論 0 3
  • 在之前的文章中分析過了多態(tài)挎春,可以知道多態(tài)本身是一種泛化機(jī)制看疙,它通過基類或者接口來設(shè)計,使程序擁有一定的靈活性直奋,但是...
    _小二_閱讀 686評論 0 0
  • 前面能庆,由于對泛型擦除的思考,引出了對Java-Type體系的學(xué)習(xí)脚线。本篇搁胆,就讓我們繼續(xù)對“泛型”進(jìn)行研究: JDK1...
    賈博巖閱讀 5,155評論 3 28
  • 文章作者:Tyan博客:noahsnail.com 1. 什么是泛型 Java泛型(Generics)是JDK 5...
    SnailTyan閱讀 773評論 0 3
  • 剛才洗完臉照鏡子,突然發(fā)現(xiàn)自己眼角有褶子了邮绿。定了定睛渠旁,發(fā)現(xiàn)是看錯了。 石站長也說我長得著急船逮,我笑笑顾腊。如果可以,我也...
    sorry_TheWorld閱讀 783評論 0 0