Java泛型詳解

泛型

泛型由來

泛型字面意思不知道是什么類型,但又好像什么類型都是。看前面用到的集合都有泛型的影子。

    public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, Serializable {
    ...
    }

以ArrayList為例,它為什么要寫成ArrayList<E>這樣.我也不知道他為什么要寫成這樣,但是我知道如果它不用泛型,那代碼就亂了,那也別寫代碼了。

  • ArrayList運用泛型可以這么寫

        ArrayList<String> strings = new ArrayList<>();//可以存String
        ArrayList<Integer> integers = new ArrayList<>();//可以存Integer類型
        ArrayList<Object> objects = new ArrayList<>();//可以存對象
    
  • ArrayList沒用泛型之后:

    如果要存放各種各樣的樣類型,是不是意味著寫各種各樣對象的鏈表,那開發者可以不用活了...咦,或者你可以可不用死了,你發現所有類都繼承自Object類,那只要這么寫一個

    在取出元素的時候強轉成對應的類型就可以了,是的,這樣就可以不會被寫代碼累死了。但為什么源碼沒有這么寫,因為它沒泛型強大!讓我們看下面代碼了解泛型的由來。

假如我要寫一個類存放一個int類型的模型,那簡單
       public class IntegerFun {
         private int data;

           public int getData() {
               return data;
           }

           public void setData(int data) {
               this.data = data;
           }
       }
滿足你的需求,但需求變了,我還要一個存放String類型的,那你也忍了,再來一個
      public class StringFun {

          private String data;

          public String getData() {
              return data;
          }

          public void setData(String data) {
              this.data = data;
          }
      }
需求又添加了一個,存放Long、Student、Math.....于是撕逼開始...結束之后,這次你聰明了,寫了一個萬能的,管它存放什么都行的類:
    public class ObjectFun {
        private Object data;

        public Object getData() {
            return data;
        }

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

這樣總算解決了問題,看用法:



你總覺得你寫的前無故人,后無來者了,可是經理還是過來找你了,因為你的程序跑不起來了,你認真的看了一下,發現代碼第十五行,存放的是Integer 結果你轉成了Float出錯了,那你可能會抱怨編譯器
沒有立即告訴你這里存在問題,接下來我們來看看運用泛型會怎么樣。

    public class Fun<T> {
        private T data;

        public T getData() {
            return data;
        }

        public void setData(T data) {
            this.data = data;
        }
    }
用法:
這就是使用泛型的原因.

多泛型

上面寫的還不夠全,因為Fun<T>只能存放一種類型的元素,假如我要存放多種呢,我希望你已經會了,再來一個泛型。
  /**
   * 泛型類
   *
   * @param <T>泛型T
   * @param <V>泛型V
   */
  public class Fun<T, V> {
      private T data;
      private V data2;

      //泛型方法
      public T getData() {
          return data;
      }

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

      public V getData2() {
          return data2;
      }

      public void setData2(V data2) {
          this.data2 = data2;
      }
  }
要存放無數個呢.....
Fun<T,T1,T2,T3,.,.>{
}

泛型規范

T1,T2,T3,.......泛型可以隨便寫嗎,可以隨便寫,但我們追求規范。
  • E — Element,常用在java Collection里,如:List<E>,Iterator<E>,Set<E>
  • K,V — Key,Value,代表Map的鍵值對
  • N — Number,數字
  • T — Type,類型,如String,Integer等等

泛型接口,泛型類,泛型方法

  • 泛型接口

      /**
       * 格式:接口名后面跟 <T>
       *
       * @param <T>
       */
      public interface IManager<T> {
          void add(T data);
    
          T remove(int index);
    
          void sop();
      }
    
  • 泛型類(之前的都是)

  • 泛型類實現泛型接口(關于怎么更好的構建泛型類,就靠諸君在日后的生涯中尋找答案了)

      /**
       * @param <T>
       */
      public class Manager<T> implements IManager<T> {
          private List<T> datas;
    
          public Manager() {
              datas = new ArrayList<>();
          }
    
          @Override
          public void add(T data) {
              datas.add(data);
          }
    
          @Override
          public T get(int index) {
              return datas.get(index);
          }
    
          @Override
          public void sop() {
              for (T t : datas) {
                  System.out.println(t);
              }
          }
      }
    
  • 泛型方法(前面的好多)

         @Override
          public T get(int index) {
               return datas.get(index);
          }
    
          //泛型方法
           public T getData() {
               return data;
           }
    
案例運行
     public class Demo {

         public static void main(String[] args) {
             Manager<Student> manager = new Manager<Student>();
             manager.add(new Student("小魚", 20));
             manager.add(new Student("小黑", 30));
             manager.add(new Student("SF", 21));

             System.out.println("get--->" + manager.get(1));

             manager.sop();
         }
     }
泛型能代表的太多了,是否能給它一些限制呢,答案也是肯定的。下面來看泛型的上下限。

確定上限

什么叫確定上限,字面意思就是你的上限我已經給你定好了,你不可能再超出這個范圍,那就有用到一個關鍵字 extends,我們讓 T(泛型)extends 某一個類,那是不是這個泛型的上限就被你決定了。
下面我們看代碼。

  • 定義基類

         /**
          * 基類
          */
         public class Person {
    
         int age;
         String name;
    
         public Person(String name, int age) {
             this.name = name;
             this.age = age;
         }
     }
    
  • 定義子類

      public class Child extends Person {
    
          public Child(String name, int age) {
              super(name, age);
          }
      }
    
  • 還有一個不相關的類

         public class Dog {
    
             private String name;
             private int age;
    
             public Dog(String name, int age) {
                 this.name = name;
                 this.age = age;
             }
         }
    
  • 定義泛型類

        public class Fun1<T extends Person> {//確定上限,(泛型類的建模很重要)
            private T datas;
    
            public T getDatas() {
                return datas;
            }
    
            public void setDatas(T datas) {
                this.datas = datas;
            }
        }
    
  • 運行(接收的引用類型要么是Person類,要么是Person的子類: 確定上限)


確定下限

感覺用的不多,關鍵字 super

案例

    public class Demo {
        public static void main(String[] args) {
            Collection<Student> cs = new ArrayList<Student>();
            cs.add(new Student("李xx", 20));
            cs.add(new Student("xxx", 19));
            cs.add(new Student("hhahah", 20));
            sop2(cs);

        }

        //接收的引用類型要么是Student類,要么是Student的父類:  確定下限
        static void sop2(Collection<? super Student> cs) {
            Iterator<?> iterator = cs.iterator();
            while (iterator.hasNext()) {
                System.out.println(iterator.next());
            }
        }
    }

讓我們帶著泛型的目光回顧 TreeSet中涉及Collections、Comparator、Comparable

我們說過TreeSet存儲的元素是要支持可排序的,那他有兩種方式,一是實現Comparable接口,二是在構造TreeSet實例的時候傳一個Comparator實例。
我們先看源碼:

  • Comparable

     package java.lang;
    
     public interface Comparable<T> {//一個泛型接口
         int compareTo(T var1);
     }
    

這就是Comparable所有的代碼,簡單吧.

  • Comparator代碼巨多,我們也就只看一行

      public interface Comparator<T> {
          int compare(T var1, T var2);
          ......
       }
    

和Comparable很像;

  • Collections集合工具類,代碼巨多,我們也就只看幾行

       public static <T extends Comparable<? super T>> void sort(List<T> var0) {
               var0.sort((Comparator)null);
           }
    
       public static <T> void sort(List<T> var0, Comparator<? super T> var1) {
            var0.sort(var1);
          }
    

當初也許你會很好奇,這個類憑什么幫你排序,現在你知道了吧,你所傳的實例都被泛型限定好了,這里出現了一個以前沒說過的"?"號,我們先忽略它。
兩個sort方法,要么實現Comparable,要么是Comparator,但有一點他們是統一的,就是都是用確定下限的泛型方式。加深印象!

案例 Comparator泛型的確定下限

  • Animal(基類)

      public class Animal {
           int age;
           String name;
    
          public Animal(int age, String name) {
              this.age = age;
              this.name = name;
          }
    
          @Override
          public String toString() {
              return "[" + this.name + "\t" + this.age + "]";
          }
      }
    
  • Cat(子類)

      public class Cat extends Animal {
    
          public Cat(int age, String name) {
              super(age, name);
          }
    
          @Override
          public String toString() {
              return super.age + "";
          }
      }
    
  • 運行


還有一個?號等著去解決...

? 通配符

我們在Collections 的源碼中看到了好多Comparable<? super T>,那這個?和T有什么關系呢。

? 和T沒有什么必然的聯系。我們要使用T,則必須在定義類的時候申明T,像 class Fun<T>,然后在類里可以使用T這一類型,
而?則表示通配(填充),表示通配,表示通配,而不是定義,因此我們使用之前不用定義它,表示通配!就如 Class<?> cls = Person.class.getClass();
Class<T>在實例化的時候,T要替換成具體類
Class<?>它是個通配泛型,?可以代表任何類型

<? extends T>受限統配,表示T的一個未知子類。
<? super T>下限統配,表示T的一個未知父類。

參考

夯實JAVA基本之一 —— 泛型詳解(1)(2):基本使用
計算機思維邏輯-泛型 (上)(中)(下)

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • 1. Java基礎部分 基礎部分的順序:基本語法,類相關的語法,內部類的語法,繼承相關的語法,異常的語法,線程的語...
    子非魚_t_閱讀 31,739評論 18 399
  • Spring Cloud為開發人員提供了快速構建分布式系統中一些常見模式的工具(例如配置管理,服務發現,斷路器,智...
    卡卡羅2017閱讀 134,837評論 18 139
  • 一、引入泛型機制的原因 假如我們想要實現一個String數組,并且要求它可以動態改變大小,這時我們都會想到用Arr...
    Q南南南Q閱讀 543評論 0 1
  • 【故事總有殘缺】 廣東的夏天總是比故鄉的夏天要長一點。 于是我并沒有像故鄉的小伙伴一樣,添上一件外套。相反我還是每...
    青微瀟瀟閱讀 405評論 1 1
  • 比較忙碌的一天 現在已經過了凌晨 應該是3.28了 可我還未休息 眼睛傳來疲憊有點睜不開眼睛 但是我想我還是堅持寫...
    薯條姐兒閱讀 225評論 0 0