Java編程思想-Chapter15-泛型

雖是讀書筆記,可是如轉載請註明出處http://segmentfault.com/blog/exploring/
..拒絕伸手複製黨java


概述

Java泛型核心概念: 告訴編譯器想使用什麼類型,而後編譯器幫你處理一切細節。segmentfault

泛型的主要目的:數組

  1. 制定容器要持有什麼類型對象,並且由編譯器來保證類型的正確性。
  2. 簡單而安全的建立複雜模型

泛型不能作什麼:不能顯示地引用運行時類型操做,例如轉型,instanceof and new表達式。(由於擦除)安全

簡單泛型(三個使用場景)

  • 一個元組類庫:建立元組,返回一組任意類型的對象。(它將一組對象直接打包存儲於其中一個單一對象)
  • 一個堆棧類:
  • RandomList :構建一個能夠應用於各類類型的對象的工具。

泛型接口

泛型方法的幾個應用場景:

  1. 槓桿利用參數推斷
  2. 可變參數(T ... args)與泛型方法(makelist實現util.Arrays.asList方法相同的功能)
    用於Generator的泛型方法: 使用泛型方法建立Generator對象,大大減小了咱們要編寫的代碼dom

    javapublic BasicGenerator(Class<T> type){
           this.type = type;
          }
     public T next(){
           try{
               return type .newInstance();
               } catch(Exception e){
               throw new RuntimeException(e);
                 }
            }
    }
    1. 簡化元組的使用 : 經過使用泛型方法整個各個Tuple類,重載static方法建立元組工具

      javapublic class Tuple {
            public static <A,B> TwoTuple<A,B> tuple(A a, B b) {
                return new TwoTuple<A,B>(a, b); }
            public static <A,B,C> ThreeTuple <A,B,C> tuple(A a, B b, C c) {
                return new ThreeTuple<A,B,C>(a, b, c);}
            public static <A,B,C,D> FourTuple<A,B,C,D> tuple(A a, B b, C c, D d) {
                return new FourTuple<A,B,C,D>(a, b, c, d); }
           }
    2. 一個Set實用工具this

泛型用於內部類和匿名內部類

構建複雜模型

使用泛型能夠簡單而安全的建立複雜模型code

擦除

Java泛型是經過擦除來實現的,namley 在使用泛型的時候,任何具體的類型信息都被擦除了,你惟一知道的就是你在使用一個對象。對象

在java泛型代碼內部,沒法得到任何有關泛型參數類型的信息。所以,你沒法知道用來建立某個特定實例的實際的類型參數。好比List和List在運行時是相同的類型。blog

擦除的正當理由是從非泛華代碼到泛華代碼的轉變過程,以及不破壞現有類庫的狀況下,將泛型融入Java語言。擦除使得現有的非泛型客戶端代碼可以在不改變的狀況下繼續使用,直至客戶端準備好用泛型重寫這些代碼。

邊界處的動做: 非泛型和泛型版本的類似的兩個類經過javap -c 命令反編譯能夠發現字節碼是相同的,就是說在運行時使用泛型的代碼和普通代碼沒有什麼區別。泛型中的全部動做都發生在邊界處—對傳遞進來的值進行額外的編譯期檢查,並插入對傳遞出去的值的轉型。這有助於澄清對擦除的混淆,所謂邊界,就是發生動做的地方。

擦除的補償

泛型不能作:

  1. instanceof
    解決方法:使用類型標籤,利用動態的isInstance判斷

    javapublic class ClassTypeCapture<T> {
          Class<T> kind; //類型標籤
           public ClassTypeCapture(Class<T> kind){
                 this.kind = kind;
          }
    }
  2. 泛型不能作:new表達式
    解決方法:傳遞一個顯示的工廠對象,限制其類型,使得只能接受實現這個工廠的類。

    javapublic interface FactoryI<T> {
          T create();
    }
    public class Foo2<T> {
           private T x ;
           //工廠
           public <F extends FactoryI<T>> Foo2(F factory){
                 x = factory.create();
          }
           public static void main(){
                 new Foo2<Integer>(new IntegerFactory());
                 new Foo2<Widget>(new Widget.Factory());
          }
    }
  3. 泛型不能作:建立數組 T[] array
    解決方法: 想要建立泛型數組的時候都使用ArrayList. orz...或者使用類型標記

若是實在想建立泛型數組,那麼惟一方式就是建立一個被擦除類型的新數組(對象數組),而後對其轉型。

javapublic class GenericArray<T> {
       private T[] array ;
       public GenericArray(int sz){
             //建立一個對象數組,而後對它轉型
             array = (T[])new Object[sz] ;
      }
}

可是這樣並非很好,由於有了擦除,數組運行時候類型就只能是Object[],若是在建立時候對其轉型爲T[],那麼編譯器該數組的實際類型就會丟失,而編譯器可能會錯過潛在的錯誤檢查。因此最爲可靠的方式:在集合內部使用Object[],而後當你使用數組元素時,添加一個對T的轉型。

javaprivate Object[] array;

public T get(int index) { 
     return (T) array[index];//Object轉型爲T
 } 

public T[] rep() {
     return (T[])array; //Object轉型爲T
}

因此最終解決辦法是使用類型標記:

javaT[] array ;
public Constructor(Class<T> type, int sz) {
array = (T[]) Array.newInstance(type, sz);
}

邊界 通配符 問題 自限定的類型 動態類型安全 異常 ....

相關文章
相關標籤/搜索