Java中的泛型/范型

維基百科中關(guān)于Java泛型的描述

Java 泛型的參數(shù)只可以代表類,不能代表個別對象槽棍。由于Java泛型的類型參數(shù)之實際類型在編譯時會被消除,所以無法在運行時得知其類型參數(shù)的類型,而且無法直接使用基本值類型作為泛型類型參數(shù)。Java編譯程序在編譯泛型時會自動加入類型轉(zhuǎn)換的編碼丧叽,故運行速度不會因為使用泛型而加快。

由于運行時會消除泛型的對象實例類型信息等缺陷經(jīng)常被人詬病公你,Java及JVM的開發(fā)方面也嘗試解決這個問題踊淳,例如Java通過在生成字節(jié)碼時添加類型推導(dǎo)輔助信息,從而可以通過反射接口獲得部分泛型信息陕靠。通過改進泛型在JVM的實現(xiàn)迂尝,使其支持基本值類型泛型和直接獲得泛型信息等。

Java允許對個別泛型的類型參數(shù)進行約束剪芥,包括以下兩種形式(假設(shè)T是泛型的類型參數(shù)垄开,C是一般類、泛類税肪,或是泛型的類型參數(shù)):

  • T實現(xiàn)接口I溉躲。
  • TC榜田,或繼承自C

泛型Since jdk1.5

摘自《Java代碼與架構(gòu)完美優(yōu)化》
泛型的本質(zhì)是參數(shù)化類型锻梳,所操作的數(shù)據(jù)類型被指定為一個參數(shù)箭券。
Java中的泛型在編譯器中實現(xiàn),而不是在虛擬機中實現(xiàn)的疑枯,虛擬機對于泛型是一無所知的辩块。因此,編譯器一定要把泛型類修改為普通類荆永,才能夠在虛擬機中運行废亭。Java中把這種技術(shù)稱為擦除,泛型代碼經(jīng)過擦除后變成原生類型具钥。

源代碼>>>泛型>>>編譯器>>>字節(jié)碼(*.class文件)>>>JVM(類裝載器豆村,字節(jié)碼校驗器,解釋器)>>>操作系統(tǒng)平臺

使用泛型的優(yōu)勢

類型安全以及不需要進行類型轉(zhuǎn)換

類型安全性:我們只能在泛型中只保存一種類型的對象氓拼。 它不允許存儲其他對象你画。因此,也不再需要進行類型轉(zhuǎn)換桃漾。下面給出使用泛型和不使用泛型的區(qū)別坏匪。

List list = new ArrayList();  
list.add("Hello Generics");  
String s = (String) list.get(0);  //需要類型轉(zhuǎn)換
List<String> list = new ArrayList<String>();  
list.add("Hello Generics");  
String s = list.get(0);   //不需要類型轉(zhuǎn)換

類型檢查從運行時挪到編譯時

編譯時檢查:在編譯時檢查,所以運行時不會出現(xiàn)問題撬统。 良好的編程策略表明适滓,在編譯時處理這個問題要比運行時好得多。

使用泛型的注意事項

  • 在static方法中不可以使用泛型恋追,泛型變量也不可以使用static關(guān)鍵字來修飾
  • 泛型常用符號的含義-T(Type)凭迹、K(Key)、V(Value)苦囱、E(Element)嗅绸,N(Number)盡管其它形式也可以作為變量的符號,但是我們習(xí)慣使用這幾種符號撕彤。
  • 泛型只適用于對象鱼鸠,基本類型不適用,但是可以使用基本類型的包裝類來實現(xiàn)羹铅,比如:
List<Integer> intList = new ArrayList<Integer>();
intList.add(1);  //自動將1轉(zhuǎn)換成Integer
intList.add(2);
  • 開發(fā)人員在使用泛型的時候蚀狰,很容易根據(jù)自己的直覺而犯一些錯誤。比如一個方法如果接收List<Object>作為形式參數(shù)职员,那么如果嘗試將一個List<String>的對象作為實際參數(shù)傳進去麻蹋,卻發(fā)現(xiàn)無法通過編譯。雖然從直覺上來說焊切,Object是String的父類扮授,這種類型轉(zhuǎn)換應(yīng)該是合理的芳室。但是實際上這會產(chǎn)生隱含的類型轉(zhuǎn)換問題,因此編譯器直接就禁止這樣的行為刹勃。http://www.infoq.com/cn/articles/cf-java-generics

泛型使用舉例

創(chuàng)建參數(shù)化類型-泛型類

//simple
class MyGen<T> {
    T obj;

    void add(T obj) {
        this.obj = obj;
    }

    T get() {
        return obj;
    }
}

public class Generics {

    public static void main(String args[]) {
        MyGen<Integer> m = new MyGen<Integer>();
        m.add(2);
        //m.add("test");   //Compile time error
        System.out.println(m.get());
    }
}

//complex from agile java
import java.util.*;
public class MultiHashMap <K,V> {
   private Map<K, List<V>> map = new HashMap<K, List<V>>();

   public static <K extends Comparable<K>,V> List<K>
         sortedKeys(MultiHashMap<K, V> map) {
      List<K> keys = new ArrayList<K>();
      keys.addAll(map.keys());
      Collections.sort(keys);
      return keys;
   }

   public Set<K> keys() {
      return map.keySet();
   }

   public int size() {
      return map.size();
   }

   public void put(K key, V value) {
      List<V> values = map.get(key);
      if (values == null) {
         values = new ArrayList<V>();
         map.put(key, values);
      }
      values.add(value);
   }

   public List<V> get(K key) {
      return map.get(key);
   }

   protected Set<Map.Entry<K, List<V>>> entrySet() {
      return map.entrySet();
   }

   public interface Filter<T> {
      boolean apply(T item);
   }

   public static <K,V> void filter(final MultiHashMap<K, ? super V> target,
                                   final MultiHashMap<K, V> source,
                                   final Filter<? super V> filter) {
      for (K key : source.keys()) {
         final List<V> values = source.get(key);
         for (V value : values)
            if (filter.apply(value))
               target.put(key, value);
      }
   }
}

泛型方法

public class Generics {

    public static < E > void printArray(E[] elements) {  
        for ( E element : elements){          
            System.out.println(element );  
         }  
         System.out.println();  
    }  
    public static void main( String args[] ) {  
        Integer[] intArray = {6, 66, 666};  
        String[] stringArray = { "6","66","666" };  
  
        System.out.println( "Printing Integer Array" );  
        printArray( intArray  );   
  
       System.out.println( "Printing String Array" );  
        printArray( stringArray );   
    }   
}

上限

每個類型參數(shù)都有一個缺省為Object的上限渤愁,你可以將類型參數(shù)限制為不同的上限。這里需要使用extends關(guān)鍵字來指定某個類型參數(shù)的上限深夯。

//from agile java
import java.util.*;
public class EventMap<K extends Date,V>
   extends MultiHashMap<K,V> {
   public List<V> getPastEvents() {
      List<V> events = new ArrayList<V>();
      for (Map.Entry<K,List<V>> entry: entrySet()) {
         K date = entry.getKey();
         if (hasPassed(date))
            events.addAll(entry.getValue());
      }
      return events;
   }

   private boolean hasPassed(K date) {
      Calendar when = new GregorianCalendar();
      when.setTime(date);
      Calendar today = new GregorianCalendar();
      if (when.get(Calendar.YEAR) != today.get(Calendar.YEAR))
         return when.get(Calendar.YEAR) < today.get(Calendar.YEAR);
      return when.get(Calendar.DAY_OF_YEAR) <
         today.get(Calendar.DAY_OF_YEAR);
   }
}

通配符wildcard

java允許使用一個通配符?來表示任意可能的類型,此外你可以使用extends子句限制通配符的上限诺苹。

import java.util.ArrayList;
import java.util.List;

abstract class Shape {
    abstract void draw();
}

class Rectangle extends Shape {
    void draw() {
        System.out.println("drawing rectangle");
    }
}

class Circle extends Shape {
    void draw() {
        System.out.println("drawing circle");
    }
}

public class Generics {
    // creating a method that accepts only child class of Shape
    public static void drawShapes(List<? extends Shape> lists) {
        for (Shape s : lists) {
            s.draw(); // calling method of Shape class by child class instance
        }
    }

    public static void main(String args[]) {
        List<Rectangle> list1 = new ArrayList<Rectangle>();
        list1.add(new Rectangle());

        List<Circle> list2 = new ArrayList<Circle>();
        list2.add(new Circle());
        list2.add(new Circle());

        drawShapes(list1);
        drawShapes(list2);
    }
}

demo來源

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末咕晋,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子收奔,更是在濱河造成了極大的恐慌掌呜,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,997評論 6 502
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件坪哄,死亡現(xiàn)場離奇詭異质蕉,居然都是意外死亡,警方通過查閱死者的電腦和手機翩肌,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,603評論 3 392
  • 文/潘曉璐 我一進店門模暗,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人念祭,你說我怎么就攤上這事兑宇。” “怎么了粱坤?”我有些...
    開封第一講書人閱讀 163,359評論 0 353
  • 文/不壞的土叔 我叫張陵隶糕,是天一觀的道長。 經(jīng)常有香客問我站玄,道長枚驻,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,309評論 1 292
  • 正文 為了忘掉前任株旷,我火速辦了婚禮再登,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘灾常。我一直安慰自己霎冯,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,346評論 6 390
  • 文/花漫 我一把揭開白布钞瀑。 她就那樣靜靜地躺著雅潭,像睡著了一般。 火紅的嫁衣襯著肌膚如雪菊碟。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,258評論 1 300
  • 那天显晶,我揣著相機與錄音,去河邊找鬼壹士。 笑死磷雇,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的躏救。 我是一名探鬼主播唯笙,決...
    沈念sama閱讀 40,122評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼盒使!你這毒婦竟也來了崩掘?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,970評論 0 275
  • 序言:老撾萬榮一對情侶失蹤少办,失蹤者是張志新(化名)和其女友劉穎苞慢,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體英妓,經(jīng)...
    沈念sama閱讀 45,403評論 1 313
  • 正文 獨居荒郊野嶺守林人離奇死亡挽放,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,596評論 3 334
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了蔓纠。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片辑畦。...
    茶點故事閱讀 39,769評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖贺纲,靈堂內(nèi)的尸體忽然破棺而出航闺,到底是詐尸還是另有隱情,我是刑警寧澤猴誊,帶...
    沈念sama閱讀 35,464評論 5 344
  • 正文 年R本政府宣布潦刃,位于F島的核電站,受9級特大地震影響懈叹,放射性物質(zhì)發(fā)生泄漏乖杠。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,075評論 3 327
  • 文/蒙蒙 一澄成、第九天 我趴在偏房一處隱蔽的房頂上張望胧洒。 院中可真熱鬧,春花似錦墨状、人聲如沸卫漫。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,705評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽列赎。三九已至,卻和暖如春镐确,著一層夾襖步出監(jiān)牢的瞬間包吝,已是汗流浹背饼煞。 一陣腳步聲響...
    開封第一講書人閱讀 32,848評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留诗越,地道東北人砖瞧。 一個月前我還...
    沈念sama閱讀 47,831評論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像嚷狞,于是被迫代替她去往敵國和親块促。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,678評論 2 354

推薦閱讀更多精彩內(nèi)容