泛型原理

一、http://blog.csdn.net/ol_beta/article/details/6192382

    本博客經過示例演示講解了List<T>,編譯器在編譯以後,T本質就是一個Object。該例子值得一看java

 

JDK1.5增長的新特性裏面有一個就是泛型。對於泛型的評價,褒貶不一,廢話很少說,先來看看他的原理。c++

泛型是提供給javac編譯器使用的,能夠限定集合中的輸入類型,讓編譯器攔截源程序中的非法輸入,編譯器編譯帶類型說明的集合時會去掉類型信息,對於參數化得泛型類型,getClass()方法的返回值和原始類型徹底同樣。編程

對於下面這個源程序: 安全

public class Oliver {
    public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<String>();
        list.add("str1");
        list.add("str2");
        String str = list.get(0);
    }
}

編譯成Oliver.class後反編譯的內容: less

public class Oliver {
    public Oliver() {
    }
    public static void main(String[] args) {
        ArrayList list = new ArrayList();
        list.add("str1");
        list.add("str2");
        String str = (String) list.get(0);
    }
}

也就是說java的泛型只是在編譯器作了參數限制,其實對性能並無什麼優化!性能

因爲編譯生成的字節碼會去掉泛型的類型信息,只要能跳過編譯器,就能夠往某個泛型集合中加入其它的類型數據。 
下面代碼展現利用反射機制跳過編譯器檢查: 優化

public class Oliver {
    public static void main(String[] args) throws Exception {
        ArrayList<Integer> list = new ArrayList<Integer>();
        list.getClass().getMethod("add", Object.class).invoke(list, "ssss");
        System.out.println("list:" + list.get(0));
    }
}

輸出結果: 
list:ssssui

 

對於java泛型,Bruce Ecke(Thinking in Java做者)曾經給出這樣的評論: 
Guess what. I really don't care. You want to call it "generics," fine, implement something that looks like C++ or Ada, that actually produces a latent typing mechanism like they do. But don't implement something whose sole purpose is to solve the casting problem in containers, and then insist that on calling it "Generics." Of course, Java has long precedence in arrogantly mangling well- accepted meanings for things: one that particularly stuck in my craw was the use of "design pattern" to describe getters and setters. In JDK 1.4 we were told some lame way to use assertions which was a backward justification for them being disabled by default. JDK 1.4 also had to invent its own inferior logging system rather than using the openly created, well-tested and well-liked Log4J. And we've also been told many times about how Java has been as fast or faster than C++, or about how one or another feature is great and flawless. I point to the threads implementation which has had major changes quietly made from version to version with not so much as a peep of apology or admission that "hey, we really screwed up here." Or maybe I was just not on that particular announcement list.
this

 二、http://irfen.iteye.com/blog/1888312   

1、Java泛型介紹spa

 

    泛型是Java 1.5的新特性,泛型的本質是參數化類型,也就是說所操做的數據類型被指定爲一個參數。這種參數類型能夠用在類、接口和方法的建立中,分別稱爲泛型類、泛型接口、泛型方法。

 

       Java泛型被引入的好處是安全簡單。

 

    在Java SE 1.5以前,沒有泛型的狀況的下,經過對類型Object的引用來實現參數的「任意化」,「任意化」帶來的缺點是要作顯式的強制類型轉換,而這種轉換是要求開發者對實際參數類型能夠預知的狀況下進行的。對於強制類型轉換錯誤的狀況,編譯器可能不提示錯誤,在運行的時候纔出現異常,這是一個安全隱患。

 優勢:

    泛型的好處是在編譯的時候檢查類型安全,

    而且全部的強制轉換都是自動和隱式的,提升代碼的重用率。

 

    泛型在使用中還有一些規則和限制:

 

一、泛型的類型參數只能是類類型(包括自定義類),不能是簡單類型。

 

二、同一種泛型能夠對應多個版本(由於參數類型是不肯定的),不一樣版本的泛型類實例是不兼容的。

 

三、泛型的類型參數能夠有多個。

 

四、泛型的參數類型可使用extends語句,例如。習慣上成爲「有界類型」。

五、泛型的參數類型還能夠是通配符類型。

 

Java代碼  
  1. Class<?> classType = Class.forName(java.lang.String);  

    泛型還有接口、方法等等,內容不少,須要花費一番功夫才能理解掌握並熟練應用。

2、Java泛型實現原理:類型擦出

       Java的泛型是僞泛型。在編譯期間,全部的泛型信息都會被擦除掉。正確理解泛型概念的首要前提是理解類型擦出(type erasure)。

       Java中的泛型基本上都是在編譯器這個層次來實現的。在生成的Java字節碼中是不包含泛型中的類型信息的。使用泛型的時候加上的類型參數,會在編譯器在編譯的時候去掉。這個過程就稱爲類型擦除。

    如在代碼中定義的List<object>和List<String>等類型,在編譯後都會編程List。JVM看到的只是List,而由泛型附加的類型信息對JVM來講是不可見的。Java編譯器會在編譯時儘量的發現可能出錯的地方,可是仍然沒法避免在運行時刻出現類型轉換異常的狀況。類型擦除也是Java的泛型實現方法與C++模版機制實現方式(後面介紹)之間的重要區別。

3、類型擦除後保留的原始類型

    原始類型(raw type)就是擦除去了泛型信息,最後在字節碼中的類型變量的真正類型。不管什麼時候定義一個泛型類型,相應的原始類型都會被自動地提供。類型變量被擦除(crased),並使用其限定類型(無限定的變量用Object)替換。

Java代碼  
  1. class Pair<T> {    
  2.   private T value;    
  3.   public T getValue() {    
  4.     return value;    
  5.   }    
  6.   public void setValue(T  value) {    
  7.     this.value = value;    
  8.   }    
  9. }    

 Pair<T>的原始類型爲:

Java代碼  
  1. class Pair {    
  2.   private Object value;    
  3.   public Object getValue() {    
  4.     return value;    
  5.   }    
  6.   public void setValue(Object  value) {    
  7.     this.value = value;    
  8.   }    
  9. }  

    由於在Pair<T>中,T是一個無限定的類型變量,因此用Object替換。其結果就是一個普通的類,如同泛型加入java變成語言以前已經實現的那樣。在程序中能夠包含不一樣類型的Pair,如Pair<String>或Pair<Integer>,可是,擦除類型後它們就成爲原始的Pair類型了,原始類型都是Object。

 

若是類型變量有限定,那麼原始類型就用第一個邊界的類型變量來替換。

好比Pair這樣聲明:

Java代碼  
  1. public class Pair<T extends Comparable& Serializable> {   

    那麼原始類型就是Comparable

 

    注意:

    若是Pair這樣聲明public class Pair<T extends Serializable&Comparable> ,那麼原始類型就用Serializable替換,而編譯器在必要的時要向Comparable插入強制類型轉換。爲了提升效率,應該將標籤(tagging)接口(即沒有方法的接口)放在邊界限定列表的末尾。

    要區分原始類型和泛型變量的類型

    在調用泛型方法的時候,能夠指定泛型,也能夠不指定泛型。

    在不指定泛型的狀況下,泛型變量的類型爲 該方法中的幾種類型的同一個父類的最小級,直到Object。

    在指定泛型的時候,該方法中的幾種類型必須是該泛型實例類型或者其子類。

Java代碼  
  1. public class Test{    
  2.   public static void main(String[] args) {    
  3.     /**不指定泛型的時候*/    
  4.     int i=Test.add(1, 2); //這兩個參數都是Integer,因此T爲Integer類型    
  5.     Number f=Test.add(1, 1.2);//這兩個參數一個是Integer,以風格是Float,因此取同一父類的最小級,爲Number    
  6.     Object o=Test.add(1, "asd");//這兩個參數一個是Integer,以風格是Float,因此取同一父類的最小級,爲Object    
  7.     
  8.     /**指定泛型的時候*/    
  9.     int a=Test.<Integer>add(1, 2);//指定了Integer,因此只能爲Integer類型或者其子類    
  10.     int b=Test.<Integer>add(1, 2.2);//編譯錯誤,指定了Integer,不能爲Float    
  11.     Number c=Test.<Number>add(1, 2.2); //指定爲Number,因此能夠爲Integer和Float    
  12.   }    
  13.   
  14.   //這是一個簡單的泛型方法    
  15.   public static <T> T add(T x,T y){    
  16.     return y;    
  17.   }    
  18. }    

    其實在泛型類中,不指定泛型的時候,也差很少,只不過這個時候的泛型類型爲Object,就好比ArrayList中,若是不指定泛型,那麼這個ArrayList中能夠聽任意類型的對象。

4、C++模板實現

    雖然我不懂C++,可是我也在網上找了下C++的實現方式。

    在c++中爲每一個模板的實例化產生不一樣的類型,這一現象被稱爲「模板代碼膨脹」。

好比 vector<int>, vector<char>, vector<double>, 這裏總共會生成3份不一樣的vector代碼。

相關文章
相關標籤/搜索