Java基礎(chǔ)-泛型

Android知識總結(jié)

一、泛型基礎(chǔ)

1)润文、什么示泛型
Java泛型是JDK1.5中引入的一個新特性宽闲,泛型提供了編譯時類型安全檢查機制丘侠,該機制允許程序員在編譯時檢到非法類型庐扫。
泛型的本質(zhì)是參數(shù)類型遵湖,也就是說所操作的數(shù)據(jù)類型指定為一個參數(shù)類型缺亮。
泛型不存在與JNM虛擬機翁涤。
2)、為什么使用泛型

1、泛型可以增強編譯時錯誤檢查葵礼,減少因類型問題引發(fā)的運行異常(ClassCastException)号阿,因此泛型具有更強的類型檢查。

2鸳粉、泛型可以避免類型轉(zhuǎn)換扔涧。

    //沒使用泛型
    private void fun1(){
        List nameList = new ArrayList();
        nameList.add("XaoMing");
        String name = (String) nameList.get(0);
    }
    
    //使用泛型
    private void fun2(){
        List<String> nameList = new ArrayList();
        nameList.add("XaoMing");
        String name = nameList.get(0);
    }

3、泛型可以泛型算法赁严,增加代碼復(fù)用性扰柠。

    private <T extends Comparable<T>> int countComparable(T[] array, T t2) {
        int count = 0;
        for (T e : array){
            if (e.compareTo(t2) > 0){
                count ++;
            }
        }
        return count;
    }

3)、Java中的泛型

1疼约、泛型類
泛型類格式:
class name <T1,T2,...Tn>{}
泛型類之影響內(nèi)部的普通方法和變量
2卤档、泛型接口
泛型格式:
interface NameInterface<T> {}
3、泛型方法
定義格式:
private <K,V> boolean compare(Pair<K, V> p1, Pare<K, V>p2){}
調(diào)用格式:
Util.<K, V> compare(p1, p2)

//泛型類, 泛型接口的使用
class Men1<T, V> : MenInterface<V>{
    var name: T
    var str : V? = null

    constructor(name: T) {
        this.name = name
    }
    
    //泛型方法
    fun <E> getStr1 (str: E) {

    }
    //泛型方法程剥, 其中的T和類泛型中的T不是一個T
    fun <T> getStr2 (str: T) {

    }

    private fun <T : Comparable<T>?> countComparable(array: Array<T>, t2: T): Int {
        var count = 0
        for (e in array) {
            if (e!! > t2) {
                count++
            }
        }
        return count
    }
    override fun getMen(): V {
        return str!!
    }

    override fun setMen(men: V) {
        this.str = men
    }


}

//泛型接口的使用
class Men2 : MenInterface<String>{
    override fun getMen(): String {
        return "XaoMing"
    }

    override fun setMen(men: String) {
        TODO("Not yet implemented")
    }

}

//泛型接口
interface MenInterface<V>{
    fun getMen() : V
    fun setMen(men : V)
}

4)劝枣、常見類型變量名稱

  • E : 表示集合元素類型(在Java集合框架中廣泛運用)
  • K : 表示關(guān)鍵字類型
  • N:表示數(shù)字類型
  • V : 表示類型
  • T : 表示任意類型
  • S,U织鲸,V:第二舔腾,第三,第四個類型
  • ? :通配符搂擦,不能用在類中稳诚,可應(yīng)用在方法和參數(shù)

5)、原始類型
缺少實際類型變量的泛型就是一個原始類型
如:
java

class Box<T>{}
Box box = new Box(); //這個Box就是Box<T>的原始類型

kotlin

class GG<T>
 val gg: GG<*> = GG<Any?>()

二瀑踢、泛型限制

對泛型變量的范圍作出限制
1)扳还、單一限制
<T extends X> //表示類型的上界,類型參數(shù)是X的子類
<T super X> //表示類型的下界橱夭,類型參數(shù)是X的超類
2)氨距、多種限制
<T extends A & B & C>

  • extends 表達(dá)的意識:這里指的是廣義上的擴展,兼有類型繼承接口實現(xiàn)之意棘劣。
  • 多種限制下的格式語法要求:如果上限類型是一個類俏让,必須第一位標(biāo)識出,否著編譯錯誤茬暇。且只能由一個類首昔,多個接口
    泛型算法實現(xiàn)的關(guān)鍵:利用受限類型參數(shù)
public class A {}
public interface B {}
public interface C {}
public class D<T extends A & B & C> { }
public static <T extends A & B & C> void setData(T data) { }

kotlin

interface A{
    fun setName()
}
interface B{
    fun setSex()
}
class D<T> where T: A, T: B  {
}

三、PESC原則

//獲取的值是Number糙俗,不能設(shè)置值
List<? super Number> list1;
//設(shè)置Number和子類沙廉,獲取對象是Object
List<? extends Object> list2;

class Foot{}
class Fruit extends Foot {}
class Apple extends Fruit {}
class HongFuShi extends Apple {}
class Orange extends Fruit {}
class GenericType<T>{
    private T date;
    public T getDate() {
        return date;
    }
    public void setDate(T date) {
        this.date = date;
    }
}

class FruitTest{
    public static void print1(GenericType<? extends Fruit> fruit){
        System.out.println(fruit.getDate().toString());
    }
    public static void print2(GenericType<? super Apple> apple){
        System.out.println(apple.getDate().toString());
    }
    public static void use1(){
        print1(new GenericType<Fruit>());  //true
        print1(new GenericType<Apple>()); //true
        print1(new GenericType<HongFuShi>());  //true
        print1(new GenericType<Foot>()); //error 超過了上線
        
        GenericType<? extends Fruit> genericType = new GenericType<>();
        genericType.setDate(new Apple()); //error 不能設(shè)置數(shù)據(jù)
        Fruit date = genericType.getDate(); //只能訪問數(shù)據(jù)
    }

    public static void use2(){
        print2(new GenericType<Apple>()); //true
        print2(new GenericType<Fruit>());  //true
        print2(new GenericType<HongFuShi>()); //error 超過線下
        print2(new GenericType<Orange>()); //error 不是同一個類型
        
        //因為 Apple和下限HongFuShi可以安全轉(zhuǎn)型為Apple, Fruit不能安全轉(zhuǎn)型
        GenericType<? super Apple> genericType = new GenericType<>();
        genericType.setDate(new Apple()); //true
        genericType.setDate(new HongFuShi()); //true
        genericType.setDate(new Fruit()); //error super 設(shè)置數(shù)據(jù)臼节,只能設(shè)置自己和下限
        Object date = genericType.getDate(); //獲取的數(shù)據(jù)是Object類型
    }
}

四撬陵、泛型擦除

功能:保證了泛型不在運行時出現(xiàn)
類型消除應(yīng)用場合

  • 編譯器會把泛型類型中所有的類型參數(shù)替換成他們的上(下)限珊皿,如果沒有對應(yīng)類型作出限制,那么就會替換成Object類型巨税。因此蟋定,編譯出的字節(jié)碼僅僅包含常規(guī)類,接口和方法草添。
  • 多種限制<T extends A & B & C>擦除后用A驶兜。
  • 在必要的時候插入類型轉(zhuǎn)換以保證類型安全。
  • 生成橋方法以在擴展泛型時保持多態(tài)性远寸。
    Bridge Methods 橋方法
  • 當(dāng)編譯一個擴展參數(shù)化類的類抄淑,或一個實現(xiàn)參數(shù)化類型接口的接口時,編譯器有可能會創(chuàng)建一個合成方法驰后,名為橋方法肆资。它是類型擦除過程的一部分。
  • java 的泛型偽泛型灶芝,JVM中不支持泛型郑原,為了兼容低版本(JDK1.5以下)

五、編譯

  • 1)夜涕、用javac把java文件編譯成class文件
    javac DD.java


  • 2)犯犁、用javap反編譯class文件字節(jié)碼
    javap -c DD.class


六、知識點

1女器、) ArrayList<String> 酸役、ArrayList<Object>和ArrayList<?>是否可以相互轉(zhuǎn)化
ArrayList<String> list1 = new ArrayList<>();
ArrayList<Object> list2 = new ArrayList<>();
ArrayList<?> list3 = new ArrayList<>();
//不能同一類型,雖然String是Object的子類驾胆,但是ArrayList<String> 涣澡、ArrayList<Object>不是同一類型
list2 = list1;
//可以直接轉(zhuǎn)換,因為俏拱?是通配符
list3 = list1;

但是泛型類可以繼承或者擴展其他泛型類,比如List和ArrayList


2吼句、) 限制
class D<T> {
    private T data;
   
    public D() {
         //不能實例化類型變量
        this.data = new T(); //error
    }
    
    //泛型類的靜態(tài)上下文中類型變量失效锅必, 所以靜態(tài)域或方法里不能引用類型變量
    //引文不知道泛型類型,泛型類型是做類初始化時候才知道的
    private static T instance1(){} //error

    //靜態(tài)方法是放行方法可以
    private static <T>T instance2(T data){  //true
        return data;
    }
    //error
    private <V extends Exception> void doWork1(V v){
        try {
            
        } catch (T e){  //error 不能捕獲泛型類的實例
            
        }
    }
    //true 泛型可以拋出異常 
    private <V extends Exception> void doWork2(V v) throws V{
        try {

        } catch (Exception e){
            throw v;
        }
    }
    public static void main(String[] argc){
        //不能用基本類型實例化類型參數(shù)
        D<double> d1; //error
        //包裝類型可以作為泛型
        D<Double> d2 = new D<>(); //true

        //運行時類型查詢只適用于原始類型
        //泛型不能用 instanceof
        if (d1 instanceof D<Double>){} //error
         if (d1 instanceof D<T>){} //error

        D<String> d3 = new D<>(); 
        //因為泛型擦除惕艳,獲取泛型的原生類型進(jìn)行比較
        System.out.println(d2.getClass() == d3.getClass()); //true

        //不能創(chuàng)建參數(shù)化類型的數(shù)組
        //可以定義泛型數(shù)組搞隐,但不能new一個泛型數(shù)組
        D<String>[] d4; //true
        D<String>[] d5 = new D<String>[10]; //error
    }
}

//泛型不能繼承異常
class A<T> extends Exception { //error
}
    //無法創(chuàng)建類型化實例
    private static <E> void append(List<E> list) throws Exception{
        E elem = new E(); //compile-time error
        list.add(elem);
    }
    
    //通過反射創(chuàng)建一個參數(shù)化類型實例
    private static <E> void append(List<E> list, Class<E> clazz) throws Exception{
        E e = clazz.newInstance();
        list.add(e);
    }

七、虛擬機是如何實現(xiàn)泛型的

  • 泛型思想早在C++語言的模板(Template)中就開始生根發(fā)芽远搪,在Java語言處于還沒有出現(xiàn)泛型的版本時劣纲,只能通過Object是所有類型的父類和類型強制轉(zhuǎn)換兩個特點的配合來實現(xiàn)類型泛化。谁鳍,由于Java語言里面所有的類型都繼承于java.lang.Object癞季,所以O(shè)bject轉(zhuǎn)型成任何對象都是有可能的劫瞳。但是也因為有無限的可能性,就只有程序員和運行期的虛擬機才知道這個Object到底是個什么類型的對象绷柒。在編譯期間志于,編譯器無法檢查這個Object的強制轉(zhuǎn)型是否成功,如果僅僅依賴程序員去保障這項操作的正確性废睦,許多ClassCastException的風(fēng)險就會轉(zhuǎn)嫁到程序運行期之中伺绽。
  • 泛型技術(shù)在C#和Java之中的使用方式看似相同,但實現(xiàn)上卻有著根本性的分歧嗜湃,C#里面泛型無論在程序源碼中奈应、編譯后的IL中(Intermediate Language,中間語言购披,這時候泛型是一個占位符)杖挣,或是運行期的CLR中,都是切實存在的今瀑,List<int>與List<String>就是兩個不同的類型程梦,它們在系統(tǒng)運行期生成,有自己的虛方法表和類型數(shù)據(jù)橘荠,這種實現(xiàn)稱為類型膨脹屿附,基于這種方法實現(xiàn)的泛型稱為真實泛型。
  • Java語言中的泛型則不一樣哥童,它只在程序源碼中存在挺份,在編譯后的字節(jié)碼文件中,就已經(jīng)替換為原來的原生類型(Raw Type贮懈,也稱為裸類型)了匀泊,并且在相應(yīng)的地方插入了強制轉(zhuǎn)型代碼,因此朵你,對于運行期的Java語言來說各聘,ArrayList<int>與ArrayList<String>就是同一個類,所以泛型技術(shù)實際上是Java語言的一顆語法糖抡医,Java語言中的泛型實現(xiàn)方法稱為類型擦除躲因,基于這種方法實現(xiàn)的泛型稱為偽泛型。
  • 將一段Java代碼編譯成Class文件忌傻,然后再用字節(jié)碼反編譯工具進(jìn)行反編譯后大脉,將會發(fā)現(xiàn)泛型都不見了,程序又變回了Java泛型出現(xiàn)之前的寫法水孩,泛型類型都變回了原生類型
    public static String method(List<String> str){
        return "ok";
    }
    public static Integer method(List<Integer> str){
        return 1;
    }
  • 開發(fā)工具編譯器:檢測是否為同一個類類型镰矿,檢測方法名和參數(shù)是否相同。
  • JDK編譯器:檢測是否為同一個類類型俘种,檢測方法名秤标、返回類型和參數(shù)是否相同绝淡。

解析:在開發(fā)工具編譯器中是同一個類型,因為參數(shù)List<T>擦除后是Object對象抛杨,所以參數(shù)相同够委。JDK編譯器 中不是同種方法,因為返回類型不同怖现。

  • 上面這段代碼是不能被編譯的茁帽,因為參數(shù)List<Integer>和List<String>編譯之后都被擦除了,變成了一樣的原生類型List<E>屈嗤,擦除動作導(dǎo)致這兩種方法的特征簽名變得一模一樣潘拨。
  • 由于Java泛型的引入,各種場景(虛擬機解析饶号、反射等)下的方法調(diào)用都可能對原有的基礎(chǔ)產(chǎn)生影響和新的需求铁追,如在泛型類中如何獲取傳入的參數(shù)化類型等。因此茫船,JCP組織對虛擬機規(guī)范做出了相應(yīng)的修改琅束,引入了諸如Signature、LocalVariableTypeTable等新的屬性用于解決伴隨泛型而來的參數(shù)類型的識別問題算谈,Signature是其中最重要的一項屬性涩禀,它的作用就是存儲一個方法在字節(jié)碼層面的特征簽名,這個屬性中保存的參數(shù)類型并不是原生類型然眼,而是包括了參數(shù)化類型的信息艾船。修改后的虛擬機規(guī)范要求所有能識別49.0以上版本的Class文件的虛擬機都要能正確地識別Signature參數(shù)。
  • 另外高每,從Signature屬性的出現(xiàn)我們還可以得出結(jié)論屿岂,擦除法所謂的擦除,僅僅是對方法的Code屬性中的字節(jié)碼進(jìn)行擦除鲸匿,實際上元數(shù)據(jù)中還是保留了泛型信息爷怀,這也是我們能通過反射手段取得參數(shù)化類型的根本依據(jù)。

八带欢、泛型擦除后恢復(fù)實例

因為在編譯成CLASS是运授,在JVM有個Signature 會對泛型弱記憶;然后可以Type類中設(shè)置泛型類型洪囤,從而找到Signature 中記憶的泛型類型徒坡。

1)撕氧、導(dǎo)入google 的 gson 原理

 implementation 'com.google.code.gson:gson:2.6.2'

2)瘤缩、代碼

        Response<Date> dateResponse = new Response<>("200", "true", new Date("XaoHua"));
        String str = gson.toJson(dateResponse);
        System.out.println(str);

        /**
        * 用自定義的 TypeRefrence 代替 google 的 TypeToken
         * 有花括號{}:代表匿名內(nèi)部類,創(chuàng)建一個匿名內(nèi)部內(nèi)實力對向
         * 無花括號{}:創(chuàng)建實例對象
         */
        Response<Date> date = gson.fromJson(str, new TypeRefrence<Response<Date>>(){}.getType());
        System.out.println(date.toString());

3)伦泥、自定義gson的TypeToken類型

    /**
     * 當(dāng)構(gòu)造方法為protected剥啤, 或者  abstract class 創(chuàng)建時必須帶花括號{}
     *
     * @param <T>
     */
    abstract class TypeRefrence<T> {
        Type type;

        protected TypeRefrence() {
            //獲得泛型類型
            Type genericSuperclass = getClass().getGenericSuperclass();
            ParameterizedType parameterizedType = (ParameterizedType) genericSuperclass;
            //因為泛型類型可以定以多個A<T, v ...>所以是數(shù)組
            Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
            type = actualTypeArguments[0];
        }

        public Type getType() {
            return type;
        }
    }

4)锦溪、type類型子接口

  • TypeVariable:泛型類型變量,可以泛型上下限等信息
  • ParameterizedType:具體的泛型類型府怯,可以獲取元數(shù)據(jù)中泛型簽名類型(泛型真實類型)刻诊。
  • GenericArrayType:當(dāng)需要描述的泛型是泛型類數(shù)組時,比如List[],Map[]牺丙,此接口會作為Type的實現(xiàn)则涯。
  • WildcardType:通配符泛型,獲取上下限信息冲簿。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末粟判,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子峦剔,更是在濱河造成了極大的恐慌档礁,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,657評論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件吝沫,死亡現(xiàn)場離奇詭異呻澜,居然都是意外死亡,警方通過查閱死者的電腦和手機惨险,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,889評論 3 394
  • 文/潘曉璐 我一進(jìn)店門羹幸,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人平道,你說我怎么就攤上這事睹欲。” “怎么了一屋?”我有些...
    開封第一講書人閱讀 164,057評論 0 354
  • 文/不壞的土叔 我叫張陵窘疮,是天一觀的道長。 經(jīng)常有香客問我冀墨,道長闸衫,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,509評論 1 293
  • 正文 為了忘掉前任诽嘉,我火速辦了婚禮蔚出,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己萎坷,他們只是感情好智润,可當(dāng)我...
    茶點故事閱讀 67,562評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著趋翻,像睡著了一般。 火紅的嫁衣襯著肌膚如雪盒蟆。 梳的紋絲不亂的頭發(fā)上踏烙,一...
    開封第一講書人閱讀 51,443評論 1 302
  • 那天师骗,我揣著相機與錄音,去河邊找鬼讨惩。 笑死辟癌,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的荐捻。 我是一名探鬼主播黍少,決...
    沈念sama閱讀 40,251評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼处面!你這毒婦竟也來了仍侥?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,129評論 0 276
  • 序言:老撾萬榮一對情侶失蹤鸳君,失蹤者是張志新(化名)和其女友劉穎农渊,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體或颊,經(jīng)...
    沈念sama閱讀 45,561評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡砸紊,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,779評論 3 335
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了囱挑。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片醉顽。...
    茶點故事閱讀 39,902評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖平挑,靈堂內(nèi)的尸體忽然破棺而出游添,到底是詐尸還是另有隱情,我是刑警寧澤通熄,帶...
    沈念sama閱讀 35,621評論 5 345
  • 正文 年R本政府宣布唆涝,位于F島的核電站,受9級特大地震影響唇辨,放射性物質(zhì)發(fā)生泄漏廊酣。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,220評論 3 328
  • 文/蒙蒙 一赏枚、第九天 我趴在偏房一處隱蔽的房頂上張望亡驰。 院中可真熱鬧,春花似錦饿幅、人聲如沸凡辱。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,838評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽透乾。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間续徽,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,971評論 1 269
  • 我被黑心中介騙來泰國打工亲澡, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留钦扭,地道東北人。 一個月前我還...
    沈念sama閱讀 48,025評論 2 370
  • 正文 我出身青樓床绪,卻偏偏與公主長得像客情,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子癞己,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,843評論 2 354

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