天天看點

【Java專題】Java泛型集合詳解一、什麼是泛型?二、泛型常用類型:三、泛型類四、泛型接口五、泛型方法 六、類型通配符七、泛型擦除八、泛型與數組九、泛型和反射

一、什麼是泛型?

1、先來看不是泛型的ArrayList集合 ArrayList集合的底層是一個object[]數組,但是它跟數組比起來又有很多的優勢,它可以存很多不同類型的資料。問題出現在資料被取出來的時候,強制轉換引發ClassCastException異常。Collection雖然表面看可以存儲各種類型的對象,其實際上它隻能存儲同一種類型的資料。

/**
* @author Jason
* @create 2020-07-11 10:57
* 普通集合存在的問題
*/
public class GenericityTest01 {
  public static void main(String[] args) {
    Collection list = new ArrayList();
    list.add("Jason");
    list.add("Jack");
    list.add(222);


    //建立一個疊代器
    Iterator it = list.iterator();
    while (it.hasNext()) {
      //将集合中的内容強制轉換
      String o =(String) it.next();
      //列印出他們各自的長度
      System.out.println(o.length());
    }
  }
}
           

控制台輸出:

5
4
Exception in thread "main" java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String
    at GenericityTest01.main(GenericityTest01.java:17)
           

2、使用泛型的情況

就可以在編譯時進行文法檢查 ,避免了在運作時出現ClassCastException異常,同時也避免了類型的強制類型轉換。  

/**
* @author Jason
* @create 2020-07-11 11:47
* 泛型執行個體
*/
public class GenericityTest04 {
  public static void main(String[] args) {
    Collection<String> list = new ArrayList<String>();
    list.add("Jason");
    list.add("Jack");
    //list.add(222);

    //建立一個疊代器
    Iterator it = list.iterator();
    while (it.hasNext()) {
      //将集合中的内容強制轉換
      String o = (String) it.next();
      //列印出他們各自的長度
      System.out.println(o.length());
    }
  }
}
           

控制台輸出:

5
4
           

3、新的問題

剛我們存入的是String,而如果我們需要存入Integer,則還要單獨編寫一個ArrayList,實際上還需要為其他所有class單獨編寫一種ArrayList,需要解決這樣的問題,我們隻需要将ArrayList編寫一種模闆。實作我們想要什麼都可以。

/**
* @author Jason
* @create 2020-07-11 14:49
*
*/
public class MyClass {
  public static void main(String[] args) {
    GenericityTest05<String> strgeneric = new GenericityTest05<>("jason");
    String key1 = strgeneric.getKey();
    System.out.println("key1:" + key1);


    System.out.println("----------");
    GenericityTest05<Integer> intgeneric = new GenericityTest05<>(111);
    Integer key2 = intgeneric.getKey();
    System.out.println("key2:" + key2);
  }
}


**
* @author Jason
* @create 2020-07-11 14:42
*/
public class GenericityTest05<T> {
  private T key;


  public GenericityTest05() {
  }


  public GenericityTest05(T key) {
    this.key = key;
  }


  public T getKey() {
    return key;
  }


  public void setKey(T key) {
    this.key = key;
  }


  @Override
  public String toString() {
    return "GenericityTest05{" +
            "key=" + key +
            '}';
  }
}
           

控制台輸出:

key1:jason
----------
key2:111
           

二、泛型常用類型:

  • E - Element (在集合中使用,因為集合中存放的是元素)
  • T - Type(表示Java 類,包括基本的類和我們自定義的類)
  • K - Key(表示鍵,比如Map中的key)
  • V - Value(表示值)
  • N - Number(表示數值類型)
  • ? - (表示不确定的java類型)
  • S、U、V - 2nd、3rd、4th types

三、泛型類

泛型類:是在執行個體化類的時候指明泛型的具體類型 (1)使用文法 類名<具體的資料類型> 對象名 = new 類名<具體的資料類型>(); (2)Java1.7以後,後面的<>中的具體的資料類型可以省略不寫 類名<具體的資料類型> 對象名 = new 類名<>(); 菱形文法 (3)注意事項

  • 泛型類,如果沒有指定具體的資料類型,此時,操作類型是Object
  • 泛型的類型參數隻能是類類型,不能是基本資料類型
  • 泛型類型在邏輯上可以看成是多個不同的類型,但實際上都是相同類型

(4)泛型類執行個體:

/**
* @author Jason
* @create 2020-07-11 15:12
* 年會抽獎器
*/
public class ProductGetter<T> {
  Random random = new Random();
  //獎品
  private T product;
  //獎品池
  ArrayList<T> list = new ArrayList<>();

  //添加獎品
  public void addProduct(T t) {
    list.add(t);
  }

  //抽獎
  public T getProduct(){
    product = list.get(random.nextInt(list.size()));
    return product;
  }

  public static void main(String[] args) {
    //建立抽獎器對象
    ProductGetter<String> strProduct = new ProductGetter<>();
    String[] strProducts = {"Apple", "HuaWei", "XiaoMi", "SanXing"};
    //給抽獎器中添加獎品
    for (int i = 0; i < strProducts.length; i++) {
      strProduct.addProduct(strProducts[i]);
    }
    //抽獎
    String product1 = strProduct.getProduct();
    System.out.println("恭喜您抽中了:"+product1);

    System.out.println("---------------");

    ProductGetter<Integer> intProduct = new ProductGetter<>();
    int[] intProducts = {1000, 2000, 3000, 5000};
    for (int i = 0; i < intProducts.length; i++) {
      intProduct.addProduct(intProducts[i]);
    }
    Integer product2 = intProduct.getProduct();
    System.out.println("恭喜您抽中了:"+product2);
  }
}
           

(5)從泛型類派生子類

    • 子類也是泛型類,子類和父類的泛型類型要一緻         class ChildGeneric<T> extends Generic<T>

/**
* @author Jason
* @create 2020-07-11 15:36
*/
public class Parent<E> {
  private E value;
  public E getValue() {
    return value;
  }
  public void setValue(E value) {
    this.value = value;
  }
}

/**
* @author Jason
* @create 2020-07-11 15:39
* 子類也是泛型類
*/
public class ChildFirst<T> extends Parent<T> {
  @Override
  public T getValue() {
    return super.getValue();
  }
}

/**
* @author Jason
* @create 2020-07-11 15:41
*/
public class MyClass {
  public static void main(String[] args) {
    ChildFirst<String> childFirst = new ChildFirst<String>();
    childFirst.setValue("Jason");
    String value = childFirst.getValue();
    System.out.println(value);
  }
}
           

• 子類不是泛型類,父類要明确泛型的資料類型

 class ChildGeneric extends Generic<String>

/**
* @author Jason
* @create 2020-07-11 15:36
*/
public class Parent<E> {
  private E value;
  public E getValue() {
    return value;
  }
  public void setValue(E value) {
    this.value = value;
  }
}

/**
* @author Jason
* @create 2020-07-11 15:48
* 泛型類派生子類,如果子類不是泛型類,那麼父類要明确資料類型(建立子類的時候無法确定父類類型)
*/
public class ChildSecond extends Parent<Integer> {
  @Override
  public Integer getValue() {
    return super.getValue();
  }
  @Override
  public void setValue(Integer value) {
    super.setValue(value);
  }
}

/**
* @author Jason
* @create 2020-07-11 15:41
*/
public class MyClass {
  public static void main(String[] args) {
    ChildSecond childSecond = new ChildSecond();
    childSecond.setValue(100);
    Integer value1 = childSecond.getValue();
    System.out.println(value1);
  }
}
           

四、泛型接口

1、泛型接口的定義文法:

interface 接口名稱 <泛型辨別,泛型辨別,…> {
    泛型辨別 方法名();
    .....
}
           

2、泛型接口詳情

  • 實作類不是泛型類,接口要明确資料類型
/**
* @author Jason
* @create 2020-07-11 16:08
* 泛型接口
*/
public interface Generator<T> {
  T getKey();
}

/**
* @author Jason
* @create 2020-07-11 16:11
* 實作泛型接口的類不是泛型類,需要明确實作泛型接口的資料類型
*/
public class Apple implements Generator<String> {
  @Override
  public String getKey() {
    return "Hello Jason!";
  }
}

/**
* @author Jason
* @create 2020-07-11 16:13
*/
public class Test {
  public static void main(String[] args) {
    Apple apple = new Apple();
    String key = apple.getKey();
    System.out.println(key);
  }
}
           
  • 實作類也是泛型類,實作類和接口的泛型類型要一緻
/**
* @author Jason
* @create 2020-07-11 16:08
* 泛型接口
*/
public interface Generator<T> {
  T getKey();
}

/**
* @author Jason
* @create 2020-07-11 16:17
* 泛型類實作泛型接口,需要保證實作接口的泛型類的泛型辨別包含泛型接口的泛型辨別
*/
public class Pair<T,E> implements Generator<T> {
  private T key;
  private E value;
  public Pair(T key, E value) {
    this.key = key;
    this.value = value;
  }
  @Override
  public T getKey() {
    return key;
  }
  public E getValue() {
    return value;
  }
}

/**
* @author Jason
* @create 2020-07-11 16:13
*/
public class Test {
  public static void main(String[] args) {
    Pair<String,Integer> pair = new Pair<>("count",100);
    String key1 = pair.getKey();
    Integer value = pair.getValue();
    System.out.println(key1 + " = " + value);
  }
}
           

五、泛型方法

1、概述 泛型方法:是在調用方法的時候指明泛型的具體類型 2、文法: 修飾符 <T,E, ...> 傳回值類型 方法名(形參清單) { 方法體... } 3、說明:

  • public與傳回值中間非常重要,可以了解為聲明此方法為泛型方法。
  • 隻有聲明了的方法才是泛型方法,泛型類中的使用了泛型的成員方法并不是泛型方法。
  • < T >表明該方法将使用泛型類型T,此時才可以在方法中使用泛型類型T。
  • 與泛型類的定義一樣,此處T可以随便寫為任意辨別,常見的如T、E、K、V等形式的參數常用于表示泛型。

4、執行個體

/**
* @author Jason
* @create 2020-07-11 15:12
* 年會抽獎器
*/
public class ProductGetter<T> {
  Random random = new Random();
  //獎品
  private T product;
  //獎品池
  ArrayList<T> list = new ArrayList<>();

  //添加獎品
  public void addProduct(T t) {
    list.add(t);
  }

   //抽獎方法
   public T getProduct(){
     product = list.get(random.nextInt(list.size()));
     return product;
   }

  //定義泛型方法
  public <E> E getProduct(ArrayList<E> list) {
    return list.get(random.nextInt(list.size()));
  }

   //靜态方法方法,采用多個泛型類型
   public static <T, E, K> void printType(T t, E e, K k) {
     System.out.println(t + "\t" + t.getClass().getSimpleName());
     System.out.println(e + "\t" + e.getClass().getSimpleName());
     System.out.println(k + "\t" + k.getClass().getSimpleName());
   }

    //泛型可變參數的定義
    public static <E> void print(E... e) {
      for (int i = 0; i < e.length; i++) {
        System.out.println(e[i]);
      }
    }

  public static void main(String[] args) {
    ProductGetter<Integer> productGetter = new ProductGetter<>();
    ProductGetter<Integer> intProduct = new ProductGetter<>();
    int[] intProducts = {1000, 2000, 3000, 5000};
    for (int i = 0; i < intProducts.length; i++) {
      intProduct.addProduct(intProducts[i]);
    }
    //泛型類的成員方法的調用
    Integer product2 = intProduct.getProduct();
    System.out.println("恭喜您抽中了:"+product2);
    System.out.println(product2+"\t"+product2.getClass().getSimpleName());

    System.out.println("+++++++++++++++++");
    ArrayList<String> strArrayList = new ArrayList<>();
    strArrayList.add("筆記本電腦");
    strArrayList.add("蘋果電腦");
    strArrayList.add("華為手機");
    strArrayList.add("ipad");
    //泛型方法的調用,類型通過調用方法的時候來指定
    String product = productGetter.getProduct(strArrayList);
    System.out.println(product+"\t"+product.getClass().getSimpleName());

     System.out.println("+++++++++++++++++");

    ArrayList<Integer> intArrayList = new ArrayList<>();
    intArrayList.add(1000);
    intArrayList.add(2000);
    intArrayList.add(3000);
    intArrayList.add(5000);
    Integer product3 = productGetter.getProduct(intArrayList);
    System.out.println(product3+"\t"+product3.getClass().getSimpleName());

    System.out.println("----------------");
    //調用多個泛型類型的靜态泛型方法
    productGetter.printType(100,"jason",true);

    System.out.println("----------------");
    //調用可變參數
    productGetter.print(1, 2, 3, 4);
    System.out.println("----------------");
    productGetter.print("a","b","c");
  }
}
           

5、總結

  • 泛型方法能使方法獨立于類而産生變化
  • 如果static方法要使用泛型能力,就必須使其成為泛型方法

 六、類型通配符

1、什麼是類型通配符?

  • 類型通配符一般是使用"?"代替具體的類型實參。
  • 是以,類型通配符是類型實參,而不是類型形參。
/**
* @author Jason
* @create 2020-07-12 8:05
*/
public class Box<E> {
  private E first;
  public E getFirst() {
    return first;
  }

  public void setFirst(E first) {
    this.first = first;
  }
}

/**
* @author Jason
* @create 2020-07-12 8:06
* 通配符
*/
public class Test04 {
  public static void main(String[] args) {
    Box<Number> box = new Box<>();
    box.setFirst(100);
    showBox(box);

    //如果此時我們想傳一個Integer類型的肯定會報錯
    //即使showBox方法是Number類型或者Integer類型都是不可以的
    /*Box<Integer> box1 = new Box<>();
    box1.setFirst(200);
    showBox(box1);*/

    //如果此時我們在showBox上使用通配符會怎麼樣呢?當然問題就解決了
    Box<Integer> box1 = new Box<>();
    box1.setFirst(200);
    showBox(box1);
  }

  /*public static void showBox(Box<Number> box){
    Number first = box.getFirst();
    System.out.println(first);
  }*/

  //這裡雖然是換了通配符,但是還是會存在一個問題,需要用Object類型接收
  public static void showBox(Box<?> box){
    Object first = box.getFirst();
    System.out.println(first);
  }
}
           

2、通配符上限

文法: 類/接口<? extends 實參類型> 要求該泛型的類型,隻能是實參類型,或實參類型的子類類型

public class Animal {
}

public class Cat extends Animal {
}

public class MiniCat extends Cat {
}

/**
* @author Jason
* @create 2020-07-12 8:33
* 通配符上限
*/
public class TestUp {
  public static void main(String[] args) {
    ArrayList<Animal> animals = new ArrayList<>();
    ArrayList<Cat> cats = new ArrayList<>();
    ArrayList<MiniCat> miniCats = new ArrayList<>();

    //這裡為啥可以添加呢?原因在于他的底層源碼:public boolean addAll(Collection<? extends E> c)
    cats.addAll(cats);
    cats.addAll(miniCats);

    //這裡會報異常:原因是什麼呢?問題在于showAnimal()采用的是通配符上限
    //showAnimal(animals);
    showAnimal(cats);
    showAnimal(miniCats);
  }

  //泛型通配符上限,傳遞的集合類型,隻能是Cat或Cat的子類
  public static void showAnimal(ArrayList<? extends Cat> list) {
    //這裡是不可以添加元素的,因為ArrayList是上限通配符,此時我們是無法知道他存的是何種類型的元素
    /*list.add(new Animal());
    list.add(new Cat());*/
    for (int i = 0; i < list.size(); i++) {
      Cat cat = list.get(i);
    }
  }
}
           

3、通配符下限

文法: 類/接口<? super 實參類型> 要求該泛型的類型,隻能是實參類型,或實參類型的父類類型。 執行個體一:

/**
* @author Jason
* @create 2020-07-12 8:57
* 通配符下限
*/
public class TestDown {
  public static void main(String[] args) {
    ArrayList<Animal> animals = new ArrayList<>();
    ArrayList<Cat> cats = new ArrayList<>();
    ArrayList<MiniCat> miniCats = new ArrayList<>();

    //集合隻能是Cat或Cat的父類類型,不能是它的子類型
    showAnimal(animals);
    showAnimal(cats);
    //showAnimal(miniCats);
  }

  //類型通配符下限,要求集合隻能是Cat或Cat的父類類型
  public static void showAnimal(List<? super Cat> list) {
    //這裡可以添加元素,并且他本身和子類都能添加,for循環是object類型接收的
    list.add(new Cat());
    list.add(new MiniCat());
    //list.add(new Animal());
    for (Object o : list) {
      System.out.println(o);
    }
  }
}
           

執行個體二:

/**
* @author Jason
* @create 2020-07-12 9:23
* 下限通配符
*/
public class Test05 {
  public static void main(String[] args) {
    //這裡的TreeSet底層采用的是下限通配符:public TreeSet(Comparator<? super E> comparator)
    //我們在構造子類對象的時候,必須先構造父類對象,如果這時候比較的是父類成員的話是沒有任何問題的。
    //而如果這裡比較子類對象的話,則不可以,因為它還沒有構造出來
    //TreeSet<Cat> treeSet = new TreeSet<>(new Comparator2());
    TreeSet<Cat> treeSet = new TreeSet<>(new Comparator1());
    treeSet.add(new Cat("jack", 10));
    treeSet.add(new Cat("mimi", 14));
    treeSet.add(new Cat("jim", 11));
    treeSet.add(new Cat("bibi", 16));
    treeSet.add(new Cat("didi", 6));

    for (Cat cat : treeSet) {
      System.out.println(cat);
    }
  }
}

class Comparator1 implements java.util.Comparator<Animal> {
  @Override
  public int compare(Animal o1, Animal o2) {
    return o1.name.compareTo(o2.name);
  }
}

class Comparator2 implements Comparator<Cat> {
  @Override
  public int compare(Cat o1, Cat o2) {
    return o1.age-o2.age;
  }
}

class Comparator3 implements Comparator<MiniCat> {
  @Override
  public int compare(MiniCat o1, MiniCat o2) {
    return o1.level-o2.level;
  }
}
           

七、泛型擦除

1、概述 泛型是Java 1.5版本才引進的概念,在這之前是沒有泛型的,但是泛型代碼能夠很好地和之前版本的代碼相容。那是因為,泛型資訊隻存在于代碼編譯階段,在進入JVM之前,與泛型相關的資訊會被擦除掉,我們稱之為–類型擦除。   2、執行個體

/**
* @author Jason
* @create 2020-07-12 10:00
*/
public class Test06 {
  public static void main(String[] args) {
    ArrayList<Integer> intList = new ArrayList<>();
    ArrayList<String> strList = new ArrayList<>();

    System.out.println(intList.getClass().getSimpleName());
    System.out.println(strList.getClass().getSimpleName());

    System.out.println("=============");
    System.out.println(intList.getClass() == strList.getClass());
  }
}
           

控制台輸出:

ArrayList
ArrayList
=============
true
           

3、無限制類型擦除

  • 擦除前
/**
* @author Jason
* @create 2020-07-12 10:05
*/
public class Erasure<T> {
  private T key;

  public T getKey() {
    return key;
  }

  public void setKey(T key) {
    this.key = key;
  }
}
           
  • 擦除後
/**
* @author Jason
* @create 2020-07-12 10:05
*/
public class Erasure {
  private Object key;

  public Object getKey() {
    return key;
  }

  public void setKey(Object key) {
    this.key = key;
  }
}
           

4、有限制類型擦除

  • 擦除前
/**
* @author Jason
* @create 2020-07-12 10:05
*/
public class Erasure<T extends Number> {
  private T key;

  public T getKey() {
    return key;
  }

  public void setKey(T key) {
    this.key = key;
  }
}
           
  • 擦除後
/**
* @author Jason
* @create 2020-07-12 10:05
*/
public class Erasure {
  private Number key;

  public Number getKey() {
    return key;
  }

  public void setKey(Number key) {
    this.key = key;
  }
}
           

5、擦除方法中定義的參數

  • 擦除前
public <T extends Number> T getValue(T value) {
  return t;
}
           
  • 擦除後
public Number getValue(Number value) {
  return value;
}
           

6、橋接方法

  • 擦除前
/**
* @author Jason
* @create 2020-07-12 10:31
* 泛型接口
*/
public interface Info<T> {
  T info(T t);
}

public class InfoImpl implements Info<Integer> {
  @Override
  public Integer info(Integer integer) {
    return null;
  }
}
           
  • 擦除後
/**
* @author Jason
* @create 2020-07-12 10:31
* 泛型接口
*/
public interface Info<T> {
  Object info(Object var);
}

public class InfoImpl implements Info {
  public Integer info(Integer var) {
    return null;
  }

@Override
  public Object info(Object var) {
    return info((Integer) var);
  }
}
           

八、泛型與數組

  • 可以聲明帶泛型的數組引用,但是不能直接建立帶泛型的數組對象
/**
* @author Jason
* @create 2020-07-12 10:53
* 泛型與數組
*/
public class demo07 {
  public static void main(String[] args) {
    //不能直接建立帶泛型的數組對象
    //ArrayList<String>[] listArr = new ArrayList<>();
    ArrayList<String>[] listArr = new ArrayList[5];

    ArrayList<Integer> intList = new ArrayList<>();
    intList.add(100);

    ArrayList<String> strList = new ArrayList<>();
    strList.add("jason");

    listArr[0] = strList;
    String s = listArr[0].get(0);
    System.out.println(s);
  }
}
           
  • 可以通過java.lang.reflect.Array的newInstance(Class,int)建立T[]數組
/**
* @author Jason
* @create 2020-07-12 11:07
*/
public class Fruit<T> {
  private T[] array;

  public Fruit(Class<T> clz, int length) {
    array = (T[])Array.newInstance(clz, length);
  }

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

  public T get(int index) {
    return array[index];
  }

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

/**
* @author Jason
* @create 2020-07-12 10:53
* 泛型與數組
*/
public class Test07 {
  public static void main(String[] args) {
    Fruit<String> fruit = new Fruit<>(String.class, 3);
    fruit.put(0, "桃子");
    fruit.put(1, "栗子");
    fruit.put(2, "蘋果");
    System.out.println(Arrays.toString(fruit.getArray()));
    String s1 = fruit.get(2);
    System.out.println(s1);
  }
}
           

九、泛型和反射

1、常用泛型類:

  • Class<T>
  • Constructor<T>

2、執行個體(分為使用泛型和不使用泛型)

/**
* @author Jason
* @create 2020-07-12 11:19
*/
public class Person {
  private String name;

  public String getName() {
    return name;
  }

  public void setName(String name) {
    this.name = name;
  }
}

/**
* @author Jason
* @create 2020-07-12 11:20
*/
public class Test08 {
  public static void main(String[] args) throws Exception {
    //使用泛型
    Class<Person> personClass = Person.class;
    Constructor<Person> constructor = personClass.getConstructor();
    Person person = constructor.newInstance();

    //不使用泛型建立對象的時候是object類型的,後續還需要進行資料類型的轉化
    Class personClass1 =Person.class;
    Constructor constructor1 = personClass1.getConstructor();
    Object o = constructor1.newInstance();
  }
}
           

繼續閱讀