重學(xué)泛型

什么是泛型俗扇?

  • Java泛型(generics)是JDK5中引入的一種參數(shù)化類型特性

  • Java泛型(generics)是JDK5中引入的一個新特性又跛,泛型提供了
    編譯時類型安全檢測機制,
    該機制允許程序員在編譯時檢測到非法的類型
    泛型的本質(zhì)是參數(shù)類型,也就是說所操作的數(shù)據(jù)類型被指定為一個參數(shù)
    泛型不存在于JVM虛擬機

什么是參數(shù)化類型甘畅?

把類型當(dāng)參數(shù)一樣傳遞

數(shù)據(jù)類型只能是引用類型(泛型的副作用)

舉個例子:

  • List<T>中的”T”稱為類型參數(shù)
  • List<Person>中的"Person"稱為實際類型參數(shù)
  • "List<T>"整個成為泛型類型
  • "List<Person>"整個稱為參數(shù)化的類型ParameterizedType

為什么使用泛型,使用泛型的好處往弓?

  1. 代碼更健壯(只要編譯期沒有警告疏唾,那么運行期就不會出現(xiàn)ClassCastException)
//不使用泛型,運行期報錯
List list = new ArrayList(); 
list.add("hello");
Integer s = (String) list.get(0);// Causes a ClassCastException to be thrown.
//使用泛型函似,編譯期就檢查
List<String> list = new ArrayList(); 
list.add("hello");
Integer s = (String) list.get(0);//編譯器就會不通過
  1. 代碼更簡潔槐脏,不用強轉(zhuǎn)
//不使用泛型,需要強轉(zhuǎn)
List list = new ArrayList(); 
list.add("hello");
String s = (String) list.get(0);
//使用泛型撇寞,不需要強轉(zhuǎn)
List<String> list = new ArrayList<String>();
list.add("hello"); 
String s = list.get(0); // no cast
  1. 代碼更靈活顿天,復(fù)用
 // java.util.List中的排序方法sort,只要實現(xiàn)了Comparator接口的都可以使用這個方法
 default void sort(@Nullable Comparator<? super E> c) {
        throw new RuntimeException("Stub!");
    }

Java是如何處理泛型的

  1. 通過運行時獲取的類信息是完全一樣的蔑担。泛型類型被擦除了牌废,擦除后只剩下原始類型,如下面所示的只剩下ArrayList類型啤握。
 ArrayList<String> strings = new ArrayList<>();
 ArrayList<Integer> integers = new ArrayList<>();
 System.out.println(strings.getClass() == integers.getClass());
 //result true

泛型擦除

  • 功能:保證了泛型不在運行時出現(xiàn)
  • 類型消除應(yīng)用的場合:
    編譯器會把泛型類型中所有的類型參數(shù)替換為它們的上(下)限鸟缕,如果沒有對類型參數(shù)做出限制,那么就替換為Object類型。因此懂从,編譯出的字節(jié)碼僅僅包含了常規(guī)類授段,接口和方法。
    在必要時插入類型轉(zhuǎn)換以保持類型安全番甩。
    生成橋方法以在擴展泛型時保持多態(tài)性
  • Bridge Methods 橋方法
    當(dāng)編譯一個擴展參數(shù)化類的類畴蒲,或一個實現(xiàn)了參數(shù)化接口的接口時,編譯器有可能因此要創(chuàng)建一個合成方法对室,名為橋方法模燥。它是類型擦除過程中的一部分

用一個簡單的例子看一下Java是怎么處理泛型的

  1. 定義一個泛型接口
public interface Box<T> {
    void set(T t);
    T get();
}
  1. 利用javac命令獲取字節(jié)碼文件
public interface Box<T> {
    void set(T var1);

    T get();
}
  1. 利用javap -c命令查看生成的字節(jié)碼,我們的T變成了Object類型掩宜。
public abstract interface test3/Box {
  public abstract set(Ljava/lang/Object;)V
  public abstract get()Ljava/lang/Object;
}

  1. 我們定義一個類去實現(xiàn)這個接口
public class ConditionalBox<T> implements Box<T> {

    private List<T> items = new ArrayList<T>(10);

    public ConditionalBox() {
    }

    @Override
    public void set(T t) {
        items.add(t);
    }

    @Override
    public T get() {
        int index = items.size() - 1;
        if (index >= 0) {
            return items.get(index);
        } else {
            return null;
        }
    }
}

5.用javap -c命令查看生成的字節(jié)碼蔫骂。可以看到我們的set和get還是構(gòu)造方法牺汤,T變成了Object類型辽旋。

public class test3/ConditionalBox implements test3/Box {

  // compiled from: ConditionalBox.java

  // access flags 0x2
  // signature Ljava/util/List<TT;>;
  // declaration: items extends java.util.List<T>
  private Ljava/util/List; items

  // access flags 0x1
  public <init>()V
   L0
    LINENUMBER 10 L0
    ALOAD 0
    INVOKESPECIAL java/lang/Object.<init> ()V
   L1
    LINENUMBER 8 L1
    ALOAD 0
    NEW java/util/ArrayList
    DUP
    BIPUSH 10
    INVOKESPECIAL java/util/ArrayList.<init> (I)V
    PUTFIELD test3/ConditionalBox.items : Ljava/util/List;
   L2
    LINENUMBER 11 L2
    RETURN
   L3
    LOCALVARIABLE this Ltest3/ConditionalBox; L0 L3 0
    // signature Ltest3/ConditionalBox<TT;>;
    // declaration: this extends test3.ConditionalBox<T>
    MAXSTACK = 4
    MAXLOCALS = 1

  // access flags 0x1
  // signature (TT;)V
  // declaration: void set(T)
  public set(Ljava/lang/Object;)V
   L0
    LINENUMBER 15 L0
    ALOAD 0
    GETFIELD test3/ConditionalBox.items : Ljava/util/List;
    ALOAD 1
    INVOKEINTERFACE java/util/List.add (Ljava/lang/Object;)Z (itf)
    POP
   L1
    LINENUMBER 16 L1
    RETURN
   L2
    LOCALVARIABLE this Ltest3/ConditionalBox; L0 L2 0
    // signature Ltest3/ConditionalBox<TT;>;
    // declaration: this extends test3.ConditionalBox<T>
    LOCALVARIABLE t Ljava/lang/Object; L0 L2 1
    // signature TT;
    // declaration: t extends T
    MAXSTACK = 2
    MAXLOCALS = 2

  // access flags 0x1
  // signature ()TT;
  // declaration: T get()
  public get()Ljava/lang/Object;
   L0
    LINENUMBER 20 L0
    ALOAD 0
    GETFIELD test3/ConditionalBox.items : Ljava/util/List;
    INVOKEINTERFACE java/util/List.size ()I (itf)
    ICONST_1
    ISUB
    ISTORE 1
   L1
    LINENUMBER 21 L1
    ILOAD 1
    IFLT L2
   L3
    LINENUMBER 22 L3
    ALOAD 0
    GETFIELD test3/ConditionalBox.items : Ljava/util/List;
    ILOAD 1
    INVOKEINTERFACE java/util/List.get (I)Ljava/lang/Object; (itf)
    ARETURN
   L2
    LINENUMBER 24 L2
   FRAME APPEND [I]
    ACONST_NULL
    ARETURN
   L4
    LOCALVARIABLE this Ltest3/ConditionalBox; L0 L4 0
    // signature Ltest3/ConditionalBox<TT;>;
    // declaration: this extends test3.ConditionalBox<T>
    LOCALVARIABLE index I L1 L4 1
    MAXSTACK = 2
    MAXLOCALS = 2
}
  1. IntelligentBox<T extends Comparable<T>>實現(xiàn)Box接口。代碼如下
public class IntelligentBox<T extends Comparable<T>> implements Box<T> {

    private List<T> items = new ArrayList<T>(10);

    @Override
    public void set(T t) {
        items.add(t);
        Collections.sort(items);
    }

    @Override
    public T get() {
        int index = items.size() - 1;
        if (index >= 0) {
            return items.get(index);
        } else {
            return null;
        }
    }
}

  1. 用javap -c命令查看IntelligentBox生成的字節(jié)碼
public class test3/IntelligentBox implements test3/Box {

  // compiled from: IntelligentBox.java

  // access flags 0x2
  // signature Ljava/util/List<TT;>;
  // declaration: items extends java.util.List<T>
  private Ljava/util/List; items

  // access flags 0x1
  public <init>()V
   L0
    LINENUMBER 7 L0
    ALOAD 0
    INVOKESPECIAL java/lang/Object.<init> ()V
   L1
    LINENUMBER 9 L1
    ALOAD 0
    NEW java/util/ArrayList
    DUP
    BIPUSH 10
    INVOKESPECIAL java/util/ArrayList.<init> (I)V
    PUTFIELD test3/IntelligentBox.items : Ljava/util/List;
    RETURN
   L2
    LOCALVARIABLE this Ltest3/IntelligentBox; L0 L2 0
    // signature Ltest3/IntelligentBox<TT;>;
    // declaration: this extends test3.IntelligentBox<T>
    MAXSTACK = 4
    MAXLOCALS = 1

  // access flags 0x1
  // signature (TT;)V
  // declaration: void set(T)
  public set(Ljava/lang/Comparable;)V
   L0
    LINENUMBER 13 L0
    ALOAD 0
    GETFIELD test3/IntelligentBox.items : Ljava/util/List;
    ALOAD 1
    INVOKEINTERFACE java/util/List.add (Ljava/lang/Object;)Z (itf)
    POP
   L1
    LINENUMBER 14 L1
    ALOAD 0
    GETFIELD test3/IntelligentBox.items : Ljava/util/List;
    INVOKESTATIC java/util/Collections.sort (Ljava/util/List;)V
   L2
    LINENUMBER 15 L2
    RETURN
   L3
    LOCALVARIABLE this Ltest3/IntelligentBox; L0 L3 0
    // signature Ltest3/IntelligentBox<TT;>;
    // declaration: this extends test3.IntelligentBox<T>
    LOCALVARIABLE t Ljava/lang/Comparable; L0 L3 1
    // signature TT;
    // declaration: t extends T
    MAXSTACK = 2
    MAXLOCALS = 2

  // access flags 0x1
  // signature ()TT;
  // declaration: T get()
  public get()Ljava/lang/Comparable;
   L0
    LINENUMBER 19 L0
    ALOAD 0
    GETFIELD test3/IntelligentBox.items : Ljava/util/List;
    INVOKEINTERFACE java/util/List.size ()I (itf)
    ICONST_1
    ISUB
    ISTORE 1
   L1
    LINENUMBER 20 L1
    ILOAD 1
    IFLT L2
   L3
    LINENUMBER 21 L3
    ALOAD 0
    GETFIELD test3/IntelligentBox.items : Ljava/util/List;
    ILOAD 1
    INVOKEINTERFACE java/util/List.get (I)Ljava/lang/Object; (itf)
    CHECKCAST java/lang/Comparable
    ARETURN
   L2
    LINENUMBER 23 L2
   FRAME APPEND [I]
    ACONST_NULL
    ARETURN
   L4
    LOCALVARIABLE this Ltest3/IntelligentBox; L0 L4 0
    // signature Ltest3/IntelligentBox<TT;>;
    // declaration: this extends test3.IntelligentBox<T>
    LOCALVARIABLE index I L1 L4 1
    MAXSTACK = 2
    MAXLOCALS = 2

  // access flags 0x1041
  public synthetic bridge get()Ljava/lang/Object;
   L0
    LINENUMBER 7 L0
    ALOAD 0
    INVOKEVIRTUAL test3/IntelligentBox.get ()Ljava/lang/Comparable;
    ARETURN
   L1
    LOCALVARIABLE this Ltest3/IntelligentBox; L0 L1 0
    // signature Ltest3/IntelligentBox<TT;>;
    // declaration: this extends test3.IntelligentBox<T>
    MAXSTACK = 1
    MAXLOCALS = 1

  // access flags 0x1041
  public synthetic bridge set(Ljava/lang/Object;)V
   L0
    LINENUMBER 7 L0
    ALOAD 0
    ALOAD 1
    CHECKCAST java/lang/Comparable
    INVOKEVIRTUAL test3/IntelligentBox.set (Ljava/lang/Comparable;)V
    RETURN
   L1
    LOCALVARIABLE this Ltest3/IntelligentBox; L0 L1 0
    // signature Ltest3/IntelligentBox<TT;>;
    // declaration: this extends test3.IntelligentBox<T>
    MAXSTACK = 2
    MAXLOCALS = 2
}

  • 可以看到檐迟,有兩處地方進(jìn)行強制類型轉(zhuǎn)換补胚,分別是get和set方法。
 INVOKEINTERFACE java/util/List.get (I)Ljava/lang/Object; (itf)
CHECKCAST java/lang/Comparable
CHECKCAST java/lang/Comparable
INVOKEVIRTUAL test3/IntelligentBox.set (Ljava/lang/Comparable;)V
  • 可以看到有兩個橋方法
  public synthetic bridge set(Ljava/lang/Object;)V
  public synthetic bridge get()Ljava/lang/Object;
  • 用偽代碼來表示IntelligentBox的過程
public class test3/IntelligentBox implements test3/Box {
    public void set(Comparable t) { /* compiled code */ }

    public Comparable get() { /* compiled code */ }
    
    @Overide
    public synthetic bridge get(){
    }
    
    @Overide
    public synthetic bridge set(Object t){
        set((Comparable)t)
    }
}

泛型擦除的殘留

看一下Box的字節(jié)碼文件Box.class和查看生成的字節(jié)碼

public interface Box<T> {
    void set(T var1);
    T get();
}

  • 疑問:不是類型擦除之后變成Object了嗎追迟?怎么這里字節(jié)碼文件還是T類型溶其?其實這里看到的其實是簽名而已,還保留定義的格式敦间,對于分析字節(jié)碼有好處瓶逃。并不是真的擦除了,保存在類的常量池中廓块。
/**
 * ParameterizedType
 * 具體的范型類型, 如Map<String, String>
 * 有如下方法:
 *
 * Type getRawType(): 返回承載該泛型信息的對象, 如上面那個Map<String, String>承載范型信息的對象是Map
 * Type[] getActualTypeArguments(): 返回實際泛型類型列表, 如上面那個Map<String, String>實際范型列表中有兩個元素, 都是String
 * Type getOwnerType(): 返回是誰的member.(上面那兩個最常用)
 */
public class TestType {
    Map<String, String> map;
    //擦除 其實在類常量池里面保留了泛型信息
    public static void main(String[] args) throws Exception {
        Field f = TestType.class.getDeclaredField("map");
        System.out.println(f.getGenericType());                               // java.util.Map<java.lang.String, java.lang.String>
        System.out.println(f.getGenericType() instanceof ParameterizedType);  // true
        ParameterizedType pType = (ParameterizedType) f.getGenericType();
        System.out.println(pType.getRawType());                               // interface java.util.Map
        for (Type type : pType.getActualTypeArguments()) {
            System.out.println(type);                                         // 打印兩遍: class java.lang.String
        }
        System.out.println(pType.getOwnerType());                             // null
    }
}

  • java虛擬機規(guī)范中為了響應(yīng)在泛型類中如何獲取傳入的參數(shù)化類型等問題厢绝,引入了signature,LocalVariableTypeTable等新的屬性來記錄泛型信息带猴,所以所謂的泛型類型擦除昔汉,僅僅是對方法的code屬性中的字節(jié)碼進(jìn)行擦除,而原數(shù)據(jù)中還是保留了泛型信息的拴清,這些信息被保存在class字節(jié)碼的常量池中靶病,使用了泛型的代碼調(diào)用處會生成一個signature簽名字段,signature指明了這個常量在常量池的地址贷掖,這樣我們就找到了參數(shù)化類型嫡秕。這樣我們也知道 現(xiàn)在就明白了泛型擦除不是擦除全部

總結(jié)

  • QUESTION:Java泛型的原理?什么是泛型擦除機制苹威?

  • ANSWER:Java的泛型是JDK5新引入的特性,為了向下兼容驾凶,虛擬機其實是不支持泛型牙甫,所以Java實現(xiàn)的是一種偽泛型機制掷酗,也就是說Java在編譯期擦除了所有的泛型信息,這樣Java就不需要產(chǎn)生新的類型到字節(jié)碼窟哺,所有的泛型類型最終都是一種原始類型泻轰,在Java運行時根本就不存在泛型信息。

  • QUESTION:Java編譯器具體是如何擦除泛型的

  • ANSWER:

    1. 檢查泛型類型且轨,獲取目標(biāo)類型
    2. 擦除類型變量浮声,并替換為限定類型
      如果泛型類型的類型變量沒有限定(<T>),則用Object作為原始類型。如果有限定(<T extends XClass>),則用XClass作為原始類型如果有多個限定(T extends XClass1&XClass2),則使用第一個邊界XClass1作為原始類
    3. 在必要時插入類型轉(zhuǎn)換以保持類型安全
    4. 生成橋方法以在擴展時保持多態(tài)性

使用泛型以及泛型擦除帶來的影響(副作用)

泛型類型變量不能使用基本數(shù)據(jù)類型

比如沒有ArrayList<int>,只有ArrayList<Integer>.當(dāng)類型擦除后旋奢,ArrayList的原始類中的類型變量(T)替換成Object,但Object類型不能存放int值

//error報錯泳挥,因為擦除后變成了Object,而Object是無法存放int
ArrayList<int> ints = new ArrayList<int>();
ArrayList<Integer> integerArrayList = new ArrayList<Integer>();

不能使用instanceof 運算符

因為擦除后至朗,ArrayList<String>只剩下原始類型屉符,泛型信息String不存在了,所有沒法使用instanceof

ArrayList<String> stringArrayList = new ArrayList<String>();
//使用ArrayList<?>可以
if (stringArrayList instanceof ArrayList<?>){
            
}
//因為擦除ArrayList<String>后String丟失了
if (stringArrayList instanceof ArrayList<String>){

}

泛型在靜態(tài)方法和靜態(tài)類中的問題

因為泛型類中的泛型參數(shù)的實例化是在定義泛型類型對象
(比如ArrayList<Integer>)的時候指定的锹引,而靜態(tài)成員是不需要使用對象來調(diào)用的矗钟,所有對象都沒創(chuàng)建,如何確定這個泛型參數(shù)是什么

//下面兩個會報錯嫌变,因為泛型參數(shù)是要創(chuàng)建對象時確定
public static T a;
public static T test1(T t) {
}

//這里不報錯吨艇,因為這是一個泛型方法,此T非彼T test2(T t)的T
public static <T> T test2(T t) {
    return t;
}

泛型類型中的方法沖突

因為擦除后兩個equals方法變成一樣的了

//方法沖突腾啥,因為擦除后變一樣了
@Override
public boolean equals(T obj) {
    return super.equals(obj);
}

@Override
public boolean equals(Object obj) {
    return super.equals(obj);
}

沒法創(chuàng)建泛型實例

因為類型不確定

class Test02 {
   //無法創(chuàng)建一個類型參數(shù)的實例秸应。下面會報錯
   public static <E> void append(List<E> list) {
      //  E elem = new E();  // compile-time error
      //  list.add(elem);
   }
   //通過反射創(chuàng)建一個參數(shù)化類型的實例
   public static <E> void append(List<E> list, Class<E> cls) throws Exception {
       E elem = cls.newInstance();   // OK
       list.add(elem);
   }
}

沒有泛型數(shù)組

因為數(shù)組是協(xié)變,擦除后就沒法滿足數(shù)組協(xié)變的原則

//        Plate<Apple>[] applePlates = new Plate<Apple>[10];//不允許
//        T[] arr = new T[10];//不允許
       Apple[] apples = new Apple[10];
       Fruit[] fruits = new Fruit[10];
        System.out.println(apples.getClass());
        //class [Lcom.zero.genericsdemo02.demo02.Apple;
        System.out.println(fruits.getClass());
        //class [Lcom.zero.genericsdemo02.demo02.Fruit;
       fruits = apples;
       // fruits里面原本是放什么類型的碑宴? Fruit or Apple
        // Apple[]
       fruits[0] = new Banana();//編譯通過软啼,運行報ArrayStoreException
        //Fruit是Apple的父類,F(xiàn)ruit[]是Apple[]的父類延柠,這就是數(shù)組的協(xié)變
        //如果加入泛型后祸挪,由于擦除機制,運行時將無法知道數(shù)組的類型
        Plate<?>[] plates = new Plate<?>[10];//這是可以的

泛型贞间,繼承和子類型

給定兩種具體的類型A和B(例如Fruit和Apple),
無論A和B是否相關(guān)贿条,
MyClass<A>與MyClass<B>都沒半毛錢關(guān)系,
它們的公共父對象是Object

泛型PECS原則

  • 如果你只需要從集合中獲得類型T , 使用<? extends T>通配符
  • 如果你只需要將類型T放到集合中, 使用<? super T>通配符
  • 如果你既要獲取又要放置元素增热,則不使用任何通配符整以。例如List<Apple>
  • PECS即 Producer extends Consumer super, 為了便于記憶峻仇。
  • 為何要PECS原則公黑?提升了API的靈活性
  • <?> 既不能存也不能取

在泛型編程時,使用部分限定的形參時,<? super T>和<? extends T>的使用場景容易混淆凡蚜, PECS原則可以幫助我們很好記住它們:提供者(Provider)使用extends人断,消費者(Consumer) 使用super。通俗地說朝蜘, Provider指的就是該容器從自己的容器里提供T類型或T的子類型的對象供別人使用恶迈; Consumer指的就是該容器把從別處拿到的T類型或T的子類型的對象放到自己的容器。

Kotlin的泛型

  • 使用關(guān)鍵字 out 來支持協(xié)變谱醇,等同于 Java 中的上界通配符 ? extends暇仲。
  • 使用關(guān)鍵字 in 來支持逆變,等同于 Java 中的下界通配符 ? super副渴。
var textViews: List<out TextView>
var textViews: List<in TextView>

聲明處的 out 和 in

Kotlin 提供了另外一種寫法:可以在聲明類的時候奈附,給泛型符號加上 out 關(guān)鍵字,表明泛型參數(shù) T 只會用來輸出佳晶,在使用的時候就不用額外加 out 了桅狠。

class Producer<out T> {
    fun produce(): T {
        ...
    }
}

val producer: Producer<TextView> = Producer<Button>() // ?? 這里不寫 out 也不會報錯
val producer: Producer<out TextView> = Producer<Button>() // ?? out 可以但沒必要

where關(guān)鍵字

Java 中聲明類或接口的時候,可以使用 extends 來設(shè)置邊界轿秧,將泛型類型參數(shù)限制為某個類型的子集中跌,同時這個邊界是可以設(shè)置多個,用 & 符號連接:

//T 的類型必須同時是 B 和 C 的子類型
class A<T extends B & C>{ 
}

在Kotlin中

//T 的類型必須同時是 B 和 C 的子類型
class A<T> where T : B, T : C

reified關(guān)鍵字

inline fun <reified T> printIfTypeMatch(item: Any) {
    if (item is T) { // ?? 這里就不會在提示錯誤了
        println(item)
    }
}

Kotlin 泛型與 Java 泛型不一致的地方

  1. Java 里的數(shù)組是支持協(xié)變的菇篡,而 Kotlin 中的數(shù)組 Array 不支持協(xié)變漩符。

這是因為在 Kotlin 中數(shù)組是用 Array 類來表示的,這個 Array 類使用泛型就和集合類一樣驱还,所以不支持協(xié)變嗜暴。

  1. Java 中的 List 接口不支持協(xié)變,而 Kotlin 中的 List 接口支持協(xié)變议蟆。

Java 中的 List 不支持協(xié)變闷沥,原因在上文已經(jīng)講過了,需要使用泛型通配符來解決咐容。

在 Kotlin 中舆逃,實際上 MutableList 接口才相當(dāng)于 Java 的 List。Kotlin 中的 List 接口實現(xiàn)了只讀操作戳粒,沒有寫操作路狮,所以不會有類型安全上的問題,自然可以支持協(xié)變蔚约。

面試常問

  1. Array中可以用泛型嗎?

答:不能

  1. 泛型類型引用傳遞問題

問:你可以把List<String>傳遞給一個接受List<Object>參數(shù)的方法嗎奄妨?

ArrayList<String> arrayList1=new ArrayList<Object>();
ArrayList<Object> arrayList1=new ArrayList<String>();

答:不能。沒有半毛錢關(guān)系

  1. Java中List<?>和List<Object>之間的區(qū)別是什么?
    答:
  • List :完全沒有類型限制和賦值限定苹祟。
  • List<Object> :看似用法與List一樣砸抛,但是在接受其他泛型賦值時會出現(xiàn)編譯錯誤评雌。
  • List<?>:是一個泛型,在沒有賦值前锰悼,表示可以接受任何類型的集合賦值柳骄,但賦值之后不能往里面隨便添加元素团赏,但可以remove和clear箕般,并非immutable(不可變)集合。List<?>一般作為參數(shù)來接收外部集合舔清,或者返回一個具體元素類型的集合丝里,也稱為通配符集合。
  1. 什么是泛型中的限定通配符和非限定通配符 ?

答:

  • 限定通配符<? extends T> <? super T>
  • 非限定通配符<?>

5.泛型類型變量不能是基本數(shù)據(jù)類型

//error
ArrayList<double> arr1 = new ArrayList<>();
ArrayList<Double> arr2 = new ArrayList<>();
  1. 運行時類型查詢
ArrayList<String> arrayList=new ArrayList<String>();
if( arrayList instanceof ArrayList<String>) //擦除
if( arrayList instanceof ArrayList<?>)  

  1. Java 的泛型本身是不支持協(xié)變和逆變的
  • 可以使用泛型通配符 ? extends 來使泛型支持協(xié)變体谒,但是「只能讀取不能修改」杯聚,這里的修改僅指對泛型集合添加元素,如果是 remove(int index) 以及 clear 當(dāng)然是可以的抒痒。
  • 可以使用泛型通配符 ? super 來使泛型支持逆變幌绍,但是「只能修改不能讀取」,這里說的不能讀取是指不能按照泛型類型讀取故响,你如果按照 Object 讀出來再強轉(zhuǎn)當(dāng)然也是可以的傀广。
  1. Java中數(shù)組是協(xié)變的
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市彩届,隨后出現(xiàn)的幾起案子伪冰,更是在濱河造成了極大的恐慌,老刑警劉巖樟蠕,帶你破解...
    沈念sama閱讀 219,188評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件贮聂,死亡現(xiàn)場離奇詭異,居然都是意外死亡寨辩,警方通過查閱死者的電腦和手機吓懈,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,464評論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來靡狞,“玉大人耻警,你說我怎么就攤上這事∷H粒” “怎么了榕栏?”我有些...
    開封第一講書人閱讀 165,562評論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長蕾各。 經(jīng)常有香客問我扒磁,道長,這世上最難降的妖魔是什么式曲? 我笑而不...
    開封第一講書人閱讀 58,893評論 1 295
  • 正文 為了忘掉前任妨托,我火速辦了婚禮缸榛,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘兰伤。我一直安慰自己内颗,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,917評論 6 392
  • 文/花漫 我一把揭開白布敦腔。 她就那樣靜靜地躺著均澳,像睡著了一般。 火紅的嫁衣襯著肌膚如雪符衔。 梳的紋絲不亂的頭發(fā)上找前,一...
    開封第一講書人閱讀 51,708評論 1 305
  • 那天,我揣著相機與錄音判族,去河邊找鬼躺盛。 笑死,一個胖子當(dāng)著我的面吹牛形帮,可吹牛的內(nèi)容都是我干的槽惫。 我是一名探鬼主播,決...
    沈念sama閱讀 40,430評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼辩撑,長吁一口氣:“原來是場噩夢啊……” “哼界斜!你這毒婦竟也來了适掰?” 一聲冷哼從身側(cè)響起剥纷,我...
    開封第一講書人閱讀 39,342評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎坦仍,沒想到半個月后水慨,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體得糜,經(jīng)...
    沈念sama閱讀 45,801評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,976評論 3 337
  • 正文 我和宋清朗相戀三年晰洒,在試婚紗的時候發(fā)現(xiàn)自己被綠了朝抖。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,115評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡谍珊,死狀恐怖治宣,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情砌滞,我是刑警寧澤侮邀,帶...
    沈念sama閱讀 35,804評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站贝润,受9級特大地震影響绊茧,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜打掘,卻給世界環(huán)境...
    茶點故事閱讀 41,458評論 3 331
  • 文/蒙蒙 一华畏、第九天 我趴在偏房一處隱蔽的房頂上張望鹏秋。 院中可真熱鬧,春花似錦亡笑、人聲如沸侣夷。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,008評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽百拓。三九已至,卻和暖如春绝骚,著一層夾襖步出監(jiān)牢的瞬間耐版,已是汗流浹背祠够。 一陣腳步聲響...
    開封第一講書人閱讀 33,135評論 1 272
  • 我被黑心中介騙來泰國打工压汪, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人古瓤。 一個月前我還...
    沈念sama閱讀 48,365評論 3 373
  • 正文 我出身青樓止剖,卻偏偏與公主長得像,于是被迫代替她去往敵國和親落君。 傳聞我的和親對象是個殘疾皇子穿香,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,055評論 2 355

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

  • 前言 不知道讀者們平時使用泛型多不多,自認(rèn)為對泛型了解多少呢绎速?本文筆者帶你重學(xué)一下泛型皮获,不只從語法的角度,盡可能從...
    樹獺非懶閱讀 1,673評論 1 1
  • 不詩意的女程序媛不是好廚師~轉(zhuǎn)載請注明出處纹冤,F(xiàn)rom李詩雨---https://blog.csdn.net/cjm...
    倔脾氣的皮皮蝦啊閱讀 476評論 0 1
  • 建議先閱讀我的上一篇文章 -- Java 泛型 和 Java 泛型一樣洒宝,Kotlin 泛型也是 Kotlin 語言...
    JohnnyShieh閱讀 6,500評論 1 26
  • 通配符 1.數(shù)組具有協(xié)變性:可以向?qū)С鲱愋偷臄?shù)組賦予基類型的數(shù)組引用: 上面的代碼不會出現(xiàn)編譯問題,因為Apple...
    zpauly閱讀 704評論 0 0
  • 久違的晴天萌京,家長會雁歌。 家長大會開好到教室時,離放學(xué)已經(jīng)沒多少時間了知残。班主任說已經(jīng)安排了三個家長分享經(jīng)驗靠瞎。 放學(xué)鈴聲...
    飄雪兒5閱讀 7,523評論 16 22