1. 程式人生 > 其它 >JAVA基礎複習-泛型

JAVA基礎複習-泛型

泛型

1、定義

集合容器類設計階段、宣告階段不能確定這個容器到底實際存的是什麼型別的物件,所以在JDK1.5之前只能把元素型別設計為Object,JDK1.5之後使用泛型來解決。因為在這個時候,除了元素的型別不確定,其他的部分是確定的,例如關於這個元素如何儲存、如何管理等是確定的,因此此時把元素的型別設計成一個引數,這個型別引數就叫做泛型。Collention,List,這個E就是型別引數,即泛型。

2、概念

所謂泛型,即允許在定義類、介面時,通過一個標識表示類中某個屬性的型別或者是某個方法的返回值及引數型別。這個型別引數將在使用時確定(例如,繼承或實現這個介面;用這個型別宣告變數、建立物件時)。

3、引數化型別

JDK1.5後引入的概念,即允許我們在建立集合物件時再指定集合元素的型別,比如List, 例項化集合物件時使用List

4、使用示例

  • 在集合中使用泛型

    1. 集合介面、集合類在jdk5.0時修改為帶泛型的結構;
    2. 在例項化集合類時,可以指明具體的泛型型別;
    3. 指明泛型型別後,在集合類、集合介面中,凡是定義類、介面時,內部結構(方法、構造器、屬性等)使用到類的泛型的位置,都需要指定為例項化的泛型型別;
    4. 泛型的型別必須是類,不能是基本資料型別,需要用到基本資料型別的位置,使用其包裝類。
    5. 如果例項化時沒有指明泛型的型別,預設型別為java.lang.Object型別,不建議這樣使用,所以例項化時要指明泛型的型別
  • 如何自定義泛型結構

    1. 自定義泛型類、泛型介面

      泛型類不能時靜態的,因為靜態類是在類例項化之前就初始化了,而此時泛型型別還不確定,初始化失敗。

      /**
       * 自定義泛型類
       *
       * @param <T> 自定義時,可以將泛型視為一個具體的類,這樣容易理解
       */
      public class Person<T> {
          private String sex;
          private Integer age;
          //類的內部結構就可以使用類的泛型(屬性、方法、引數等)
          private T personT;
      
          public Person() {
          }
          public Person(String sex, Integer age, T personT) {
              this.sex = sex;
              this.age = age;
              this.personT = personT;
          }
          public String getSex() {
              return sex;
          }
          public void setSex(String sex) {
              this.sex = sex;
          }
          public Integer getAge() {
              return age;
          }
          public void setAge(Integer age) {
              this.age = age;
          }
          public T getPersonT() {
              return personT;
          }
          public void setPersonT(T personT) {
              this.personT = personT;
          }
          @Override
          public String toString() {
              return "Person{" +
                      "sex='" + sex + '\'' +
                      ", age=" + age +
                      ", personT=" + personT +
                      '}';
          }
      }
      
      /**
       * 泛型子類
       * 繼承泛型父類時,父類指定泛型型別,子類例項化時,就不用再指定泛型型別了。
       */
      public class man extends Person<Integer> {
      
      }
      
      /**
       * 泛型子類
       * 繼承泛型父類時,父類不指定泛型型別,子類例項化時,需要指定泛型型別了。
       */
      public class woman<T> extends Person<T> {
      
      }
      
    2. 自定義泛型方法

      泛型方法定義:在方法中出現了泛型的結構。注:方法的泛型引數與類的泛型引數沒有任何關係。

      // 自定義泛型方法
      public <E> List<E> copyFromArrayToList(E[] arr) {
      	List<E> list = new ArrayList<>();
          for (E e : list) {
              list.add(e);
          }
      	return list;
      }
      

      如何理解方法的返回值List前面還增加了一個

      答:告訴編譯器E是一個泛型,不是一個引數型別的類,如果泛型方法前不增加,編譯器會認為E是一個引數型別的類,就會去找這個類,此時是找不到的,所以編譯器會提示錯誤。

      // 錯誤的泛型方法寫法(返回值前沒有加泛型)
      public List<E> copyFromArrayToList(E[] arr) {
          List<E> list = new ArrayList<>();
          for (E e : list) {
              list.add(e);
          }
          return list;
      }
      

      靜態泛型方法:泛型引數是在呼叫方法時確定的,並非在例項化類時確定,所以可以有靜態泛型方法。

      /**
       * 靜態泛型方法
       */
      public static <E> List<E> copyFromArrayToList2(E[] arr) {
          List<E> list = new ArrayList<>();
          for (E e : list) {
              list.add(e);
          }
          return list;
      }
      

5、萬用字元?

  • A與B是父子類關係,那G<'A'>與G<'B'>是並列關係,非父子類關係,G<?>是他們的根父類
/**
 * 萬用字元?在集合中的使用示例
 * List<?>是List<Object>與List<String>的父類,而List<Object>與List<String>是並列關係,非父子類關係
 * 結論:A與B是父子類關係,那G<A>與G<B>是並列關係,非父子類關係,G<?>是他們的根父類
 */
public static void main(String[] args) {
    List<Object> list1 = new ArrayList<>(Arrays.asList(1, 2, 3));
    List<String> list2 = new ArrayList<>(Arrays.asList("a", "b", "c"));
    List<?> list = new ArrayList<>(Arrays.asList(1, 2, 3));
    print(list1);
    print(list2);
    print(list);
}

private static void print(List<?> list) {
    Iterator<?> iterator = list.iterator();
    while (iterator.hasNext()) {
        Object obj = iterator.next();
        System.out.println(obj);
    }
}
  • G<?>集合(使用萬用字元的集合物件)資料的操作

    1. 讀取資料操作,返回值為java.lang.Object,因為Object時所有類的父類;
    2. 新增資料操作,不能向G<?>集合中新增資料,除了新增null物件,只能做讀取操作
  • 有限制條件的萬用字元的使用

    /**
     * 有限制條件的萬用字元的使用
     * 萬用字元做子類繼承時,萬用字元佔位的型別必須是Animal的子類,包含Animal
     * 萬用字元做超類時,萬用字元佔位的型別必須是Animal的父類,包含Animal
     */
    public static void main(String[] args) {
        List<? extends Animal> list1 = null;
        List<? super Animal> list2 = null;
    
        List<Dog> list3 = new ArrayList<>();
        List<Animal> list4 = new ArrayList<>();
        List<Object> list5 = new ArrayList<>();
    
        list1 = list3;
        list1 = list4;
        //list1 = list5;//報錯,不能賦值,因為Object是Animal的父類
    
        //list2 = list3;//報錯,不能賦值,因為Dog是Animal的子類
        list2 = list4;
        list2 = list5;
        
        /**
         * 取值操作
         */
        //萬用字元做子類繼承時,集合取值後,返回值不能低於(<=)父類
        Animal animal = list1.get(0);
        Object obj = list1.get(0);
        //萬用字元做超類時,集合取值後,返回值必須高於父類(>)
        Object object = list2.get(0);
    
        /**
         * 填充值操作
         */
        //list1.add(new Object());//報錯,不能填充
        //list1.add(new Animal());//報錯,不能填充
        //list1.add(new Dog());//報錯,不能填充
    
        //list2.add(new Object());//報錯,不能填充
        list2.add(new Animal());
        list2.add(new Dog());
    }