泛型

1. 泛型概述

泛型(Generic type 或者 generics)是對(duì) Java 語(yǔ)言的類(lèi)型系統(tǒng)的一種擴(kuò)展,以支持創(chuàng)建可以按類(lèi)型進(jìn)行參數(shù)化的類(lèi)。可以把類(lèi)型參數(shù)看作是使用參數(shù)化類(lèi)型時(shí)指定的類(lèi)型的一個(gè)占位符,就像方法的形式參數(shù)是運(yùn)行時(shí)傳遞的值的占位符一樣。

泛型是Java SE 1.5的新特性,泛型的本質(zhì)是參數(shù)化類(lèi)型,也就是說(shuō)所操作的數(shù)據(jù)類(lèi)型被指定為一個(gè)參數(shù)。這種參數(shù)類(lèi)型可以用在類(lèi)、接口和方法的創(chuàng)建中,分別稱(chēng)為泛型類(lèi)、泛型接口、泛型方法。 Java語(yǔ)言引入泛型的好處是安全簡(jiǎn)單。

在Java SE 1.5之前,沒(méi)有泛型的情況的下,通過(guò)對(duì)類(lèi)型Object的引用來(lái)實(shí)現(xiàn)參數(shù)的“任意化”,“任意化”帶來(lái)的缺點(diǎn)是要做顯式的強(qiáng)制類(lèi)型轉(zhuǎn)換,而這種轉(zhuǎn)換是要求開(kāi)發(fā)者對(duì)實(shí)際參數(shù)類(lèi)型可以預(yù)知的情況下進(jìn)行的。對(duì)于強(qiáng)制類(lèi)型轉(zhuǎn)換錯(cuò)誤的情況,編譯器可能不提示錯(cuò)誤,在運(yùn)行的時(shí)候才出現(xiàn)異常,這是一個(gè)安全隱患。

泛型的好處是在編譯的時(shí)候檢查類(lèi)型安全,并且所有的強(qiáng)制轉(zhuǎn)換都是自動(dòng)和隱式的,以提高代碼的重用率。

可以在集合框架(Collection framework)中看到泛型的動(dòng)機(jī)。例如,Map 類(lèi)允許您向一個(gè) Map添加任意類(lèi)的對(duì)象,即使最常見(jiàn)的情況是在給定映射(map)中保存某個(gè)特定類(lèi)型(比如 String)的對(duì)象。

因?yàn)?Map.get() 被定義為返回 Object,所以一般必須將 Map.get() 的結(jié)果強(qiáng)制類(lèi)型轉(zhuǎn)換為期望的類(lèi)型,如下面的代碼所示:

  Map m = new HashMap();
  m.put("key", "blarg");
  String s = (String) m.get("key");

要讓程序通過(guò)編譯,必須將 get() 的結(jié)果強(qiáng)制類(lèi)型轉(zhuǎn)換為 String,并且希望結(jié)果真的是一個(gè) String。但是有可能某人已經(jīng)在該映射中保存了不是 String 的東西,這樣的話,上面的代碼將會(huì)拋出 ClassCastException。

理想情況下,您可能會(huì)得出這樣一個(gè)觀點(diǎn),即 m 是一個(gè) Map,它將 String 鍵映射到 String 值。這可以讓您消除代碼中的強(qiáng)制類(lèi)型轉(zhuǎn)換,同時(shí)獲得一個(gè)附加的類(lèi)型檢查層,該檢查層可以防止有人將錯(cuò)誤類(lèi)型的鍵或值保存在集合中。這就是泛型所做的工作。

package cn.itcast_01;

import java.util.ArrayList;
import java.util.Iterator;

/*
 * ArrayList存儲(chǔ)字符串并遍歷
 * 
 * 我們按照正常的寫(xiě)法來(lái)寫(xiě)這個(gè)程序, 結(jié)果確出錯(cuò)了。
 * 為什么呢?
 *      因?yàn)槲覀冮_(kāi)始存儲(chǔ)的時(shí)候,存儲(chǔ)了String和Integer兩種類(lèi)型的數(shù)據(jù)。
 *      而在遍歷的時(shí)候,我們把它們都當(dāng)作String類(lèi)型處理的,做了轉(zhuǎn)換,所以就報(bào)錯(cuò)了。
 * 但是呢,它在編譯期間卻沒(méi)有告訴我們。
 * 所以,我就覺(jué)得這個(gè)設(shè)計(jì)的不好。
 * 回想一下,我們的數(shù)組
 *      String[] strArray = new String[3];
 *      strArray[0] = "hello";
 *      strArray[1] = "world";
 *      strArray[2] = 10;
 * 集合也模仿著數(shù)組的這種做法,在創(chuàng)建對(duì)象的時(shí)候明確元素的數(shù)據(jù)類(lèi)型。這樣就不會(huì)在有問(wèn)題了。
 * 而這種技術(shù)被稱(chēng)為:泛型。
 * 
 * 泛型:是一種把類(lèi)型明確的工作推遲到創(chuàng)建對(duì)象或者調(diào)用方法的時(shí)候才去明確的特殊的類(lèi)型。參數(shù)化類(lèi)型,把類(lèi)型當(dāng)作參數(shù)一樣的傳遞。
 * 格式:
 *      <數(shù)據(jù)類(lèi)型>
 *      此處的數(shù)據(jù)類(lèi)型只能是引用類(lèi)型。
 * 好處:
 *      A:把運(yùn)行時(shí)期的問(wèn)題提前到了編譯期間
 *      B:避免了強(qiáng)制類(lèi)型轉(zhuǎn)換
 *      C:優(yōu)化了程序設(shè)計(jì),解決了黃色警告線
 */
public class GenericDemo {
    public static void main(String[] args) {
        // 創(chuàng)建
        ArrayList<String> array = new ArrayList<String>();

        // 添加元素
        array.add("hello");
        array.add("world");
        array.add("java");
        // array.add(new Integer(100));
        //array.add(10); // JDK5以后的自動(dòng)裝箱
        // 等價(jià)于:array.add(Integer.valueOf(10));

        // 遍歷
        Iterator<String> it = array.iterator();
        while (it.hasNext()) {
            // ClassCastException
            // String s = (String) it.next();
            String s = it.next();
            System.out.println(s);
        }

        // 看下面這個(gè)代碼
        // String[] strArray = new String[3];
        // strArray[0] = "hello";
        // strArray[1] = "world";
        // strArray[2] = 10;
    }
}

2. 泛型的好處

Java 語(yǔ)言中引入泛型是一個(gè)較大的功能增強(qiáng)。不僅語(yǔ)言、類(lèi)型系統(tǒng)和編譯器有了較大的變化,以支持泛型,而且類(lèi)庫(kù)也進(jìn)行了大翻修,所以許多重要的類(lèi),比如集合框架,都已經(jīng)成為泛型化的了。這帶來(lái)了很多好處:

2.1 類(lèi)型安全

泛型的主要目標(biāo)是提高 Java 程序的類(lèi)型安全。通過(guò)知道使用泛型定義的變量的類(lèi)型限制,編譯器可以在一個(gè)高得多的程度上驗(yàn)證類(lèi)型假設(shè)。沒(méi)有泛型,這些假設(shè)就只存在于程序員的頭腦中(或者如果幸運(yùn)的話,還存在于代碼注釋中)。

Java 程序中的一種流行技術(shù)是定義這樣的集合,即它的元素或鍵是公共類(lèi)型的,比如“String 列表”或者“String 到 String 的映射”。通過(guò)在變量聲明中捕獲這一附加的類(lèi)型信息,泛型允許編譯器實(shí)施這些附加的類(lèi)型約束。類(lèi)型錯(cuò)誤現(xiàn)在就可以在編譯時(shí)被捕獲了,而不是在運(yùn)行時(shí)當(dāng)作 ClassCastException 展示出來(lái)。將類(lèi)型檢查從運(yùn)行時(shí)挪到編譯時(shí)有助于您更容易找到錯(cuò)誤,并可提高程序的可靠性。

2.2 消除強(qiáng)制類(lèi)型轉(zhuǎn)換

泛型的一個(gè)附帶好處是,消除源代碼中的許多強(qiáng)制類(lèi)型轉(zhuǎn)換。這使得代碼更加可讀,并且減少了出錯(cuò)機(jī)會(huì)。
盡管減少?gòu)?qiáng)制類(lèi)型轉(zhuǎn)換可以降低使用泛型類(lèi)的代碼的羅嗦程度,但是聲明泛型變量會(huì)帶來(lái)相應(yīng)的羅嗦。

2.3 優(yōu)化了程序設(shè)計(jì),解決了黃色警告線

3. 泛型的應(yīng)用

3.1 泛型的內(nèi)部原理

泛型是提供給javac編譯器使用的,可以限定集合中的輸入類(lèi)型,讓編譯器擋住源程序中的非法輸入。但是,編譯器編譯帶類(lèi)型說(shuō)明的集合時(shí)會(huì)去除掉“類(lèi)型”信息,目的就是使程序運(yùn)行效率不受影響。因此,對(duì)于參數(shù)化的泛型類(lèi)型,getClass()方法的返回值和原始類(lèi)型完全一樣。

package com.itheima.day2;

import java.util.ArrayList;

public class GenericTest {
      
       public static void main(String[] args) {
            ArrayList<String> collection1 = new ArrayList<String>();
            ArrayList collection2 = new ArrayList();
            System. out.println(collection1.getClass() == collection2.getClass());
             //結(jié)果:true
      }
}

由于編譯生成的字節(jié)碼會(huì)去掉泛型的類(lèi)型信息,只要能跳過(guò)編譯器,就可以往某個(gè)泛型集合中加入其它類(lèi)型的數(shù)據(jù),例如,用反射得到集合,再調(diào)用其add方法即可。

package com.itheima.day2;

import java.util.ArrayList;

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

ArrayList<E>類(lèi)定義和ArrayList<Integer>類(lèi)引用中涉及如下術(shù)語(yǔ):

  • 整個(gè)稱(chēng)為ArrayList<E>泛型類(lèi)型
  • ArrayList<E>中的E稱(chēng)為類(lèi)型變量或類(lèi)型參數(shù)
  • 整個(gè)ArrayList<Integer>稱(chēng)為參數(shù)化的類(lèi)型
  • ArrayList<Integer>中的Integer稱(chēng)為類(lèi)型參數(shù)的實(shí)例或?qū)嶋H類(lèi)型參數(shù)
  • ArrayList<Integer>中的<>念著typeof
  • ArrayList稱(chēng)為原始類(lèi)型

參數(shù)化類(lèi)型與原始類(lèi)型的兼容性:參數(shù)化類(lèi)型可以引用一個(gè)原始類(lèi)型的對(duì)象,編譯報(bào)告警告,例如

Collection<String> c = new Vector();//考慮到對(duì)以前代碼的兼容性,編譯器是可以通過(guò)的

原始類(lèi)型可以引用一個(gè)參數(shù)化類(lèi)型的對(duì)象,編譯報(bào)告警告,例如

Collection c = new Vector<String>();//原來(lái)的方法接受一個(gè)集合參數(shù),新的類(lèi)型也要能傳進(jìn)去

參數(shù)化類(lèi)型不考慮類(lèi)型參數(shù)的繼承關(guān)系:

Vector<String> v = new Vector<Object>(); //錯(cuò)誤!不寫(xiě)<Object>沒(méi)錯(cuò),寫(xiě)了就是明知故犯
Vector<Object> v = new Vector<String>(); //也錯(cuò)誤!

注意:

假設(shè)Vector<String> v = new Vector<Object>();可以的話,那么以后從v中取出的對(duì)象當(dāng)作String用,而v實(shí)際指向的對(duì)象中可以加入任意的類(lèi)型對(duì)象;

假設(shè)Vector<Object> v = new Vector<String>();可以的話,那么以后可以向v中加入任意的類(lèi)型對(duì)象,而v實(shí)際指向的集合中只能裝String類(lèi)型的對(duì)象。

編譯器不允許創(chuàng)建泛型變量的數(shù)組。即在創(chuàng)建數(shù)組實(shí)例時(shí),數(shù)組的元素不能使用參數(shù)化的類(lèi)型。

例如,下面語(yǔ)句有錯(cuò)誤:

Vector<Integer> vectorList[] = new Vector<Integer>[10];

思考題:

下面的代碼會(huì)報(bào)錯(cuò)誤嗎?

Vector v1 = new Vector<String>();
Vector<Object> v = v1;

答案:編譯的時(shí)候是不會(huì)報(bào)錯(cuò)的,因?yàn)榫幾g器是一行一行按照語(yǔ)法檢查代碼的,因此不會(huì)出錯(cuò)。

4. 泛型類(lèi)

把泛型定義在類(lèi)上,格式:public class 類(lèi)名<泛型類(lèi)型1,…>,注意:泛型類(lèi)型必須是引用類(lèi)型

package cn.itcast_04;

/*
 * 泛型類(lèi)的測(cè)試
 */
public class ObjectToolDemo {
    public static void main(String[] args) {
        // ObjectTool ot = new ObjectTool();
        //
        // ot.setObj(new String("風(fēng)清揚(yáng)"));
        // String s = (String) ot.getObj();
        // System.out.println("姓名是:" + s);
        //
        // ot.setObj(new Integer(30));
        // Integer i = (Integer) ot.getObj();
        // System.out.println("年齡是:" + i);

        // ot.setObj(new String("林青霞"));
        // // ClassCastException
        // Integer ii = (Integer) ot.getObj();
        // System.out.println("姓名是:" + ii);

        System.out.println("-------------");

        ObjectTool<String> ot = new ObjectTool<String>();
        // ot.setObj(new Integer(27)); //這個(gè)時(shí)候編譯期間就過(guò)不去
        ot.setObj(new String("林青霞"));
        String s = ot.getObj();
        System.out.println("姓名是:" + s);

        ObjectTool<Integer> ot2 = new ObjectTool<Integer>();
        // ot2.setObj(new String("風(fēng)清揚(yáng)"));//這個(gè)時(shí)候編譯期間就過(guò)不去
        ot2.setObj(new Integer(27));
        Integer i = ot2.getObj();
        System.out.println("年齡是:" + i);
    }
}
//泛型類(lèi):把泛型定義在類(lèi)上
class ObjectTool<T> {
    private T obj;

    public T getObj() {
        return obj;
    }

    public void setObj(T obj) {
        this.obj = obj;
    }
}

5. 泛型方法

把泛型定義在方法上,格式:public <泛型類(lèi)型> 返回類(lèi)型 方法名(泛型類(lèi)型 .)

package cn.itcast_05;

public class ObjectToolDemo {
    public static void main(String[] args) {
        // ObjectTool ot = new ObjectTool();
        // ot.show("hello");
        // ot.show(100);
        // ot.show(true);

        // ObjectTool<String> ot = new ObjectTool<String>();
        // ot.show("hello");
        //
        // ObjectTool<Integer> ot2 = new ObjectTool<Integer>();
        // ot2.show(100);
        //
        // ObjectTool<Boolean> ot3 = new ObjectTool<Boolean>();
        // ot3.show(true);

        // 定義泛型方法后
        ObjectTool ot = new ObjectTool();
        ot.show("hello");
        ot.show(100);
        ot.show(true);
    }
}
//泛型方法:把泛型定義在方法上
class ObjectTool {
    public <T> void show(T t) {
        System.out.println(t);
    }
}

6. 泛型接口

把泛型定義在接口上,格式:public interface 接口名<泛型類(lèi)型1…>

package cn.itcast_06;

public class InterDemo {
    public static void main(String[] args) {
        // 第一種情況的測(cè)試
        // Inter<String> i = new InterImpl();
        // i.show("hello");

        // // 第二種情況的測(cè)試
        Inter<String> i = new InterImpl<String>();
        i.show("hello");

        Inter<Integer> ii = new InterImpl<Integer>();
        ii.show(100);
    }
}
//泛型接口:把泛型定義在接口上 
interface Inter<T> {
    public abstract void show(T t);
}
// 實(shí)現(xiàn)類(lèi)在實(shí)現(xiàn)接口的時(shí)候
// 第一種情況:已經(jīng)知道該是什么類(lèi)型的了

//public class InterImpl implements Inter<String> {
//
//  @Override
//  public void show(String t) {
//      System.out.println(t);
//  }
// }

// 第二種情況:還不知道是什么類(lèi)型的
class InterImpl<T> implements Inter<T> {

    @Override
    public void show(T t) {
        System.out.println(t);
    }
}

7. 泛型高級(jí)(通配符)

為了解決類(lèi)型被限制死了不能動(dòng)態(tài)根據(jù)實(shí)例來(lái)確定的缺點(diǎn),引入了“通配符泛型”,針對(duì)上面的例子,使用通配泛型格式為<? extends Collection>,“?”代表未知類(lèi)型,這個(gè)類(lèi)型是實(shí)現(xiàn)Collection接口。? extends E:向下限定,E及其子類(lèi),限定通配符的上邊界。? super E:向上限定,E及其父類(lèi),限定通配符的下邊界。

package cn.itcast_07;

import java.util.ArrayList;
import java.util.Collection;

/*
 * 泛型高級(jí)(通配符)
 * ?:任意類(lèi)型,如果沒(méi)有明確,那么就是Object以及任意的Java類(lèi)了
 * ? extends E:向下限定,E及其子類(lèi)
 * ? super E:向上限定,E極其父類(lèi)
 */
public class GenericDemo {
    public static void main(String[] args) {
        // 泛型如果明確的寫(xiě)的時(shí)候,前后必須一致
        Collection<Object> c1 = new ArrayList<Object>();
        // Collection<Object> c2 = new ArrayList<Animal>();
        // Collection<Object> c3 = new ArrayList<Dog>();
        // Collection<Object> c4 = new ArrayList<Cat>();

        // ?表示任意的類(lèi)型都是可以的
        Collection<?> c5 = new ArrayList<Object>();
        Collection<?> c6 = new ArrayList<Animal>();
        Collection<?> c7 = new ArrayList<Dog>();
        Collection<?> c8 = new ArrayList<Cat>();

        // ? extends E:向下限定,E及其子類(lèi)
        // Collection<? extends Animal> c9 = new ArrayList<Object>();
        Collection<? extends Animal> c10 = new ArrayList<Animal>();
        Collection<? extends Animal> c11 = new ArrayList<Dog>();
        Collection<? extends Animal> c12 = new ArrayList<Cat>();

        // ? super E:向上限定,E極其父類(lèi)
        Collection<? super Animal> c13 = new ArrayList<Object>();
        Collection<? super Animal> c14 = new ArrayList<Animal>();
        // Collection<? super Animal> c15 = new ArrayList<Dog>();
        // Collection<? super Animal> c16 = new ArrayList<Cat>();
    }
}

class Animal {
}

class Dog extends Animal {
}

class Cat extends Animal {
}

泛型

泛型是提供給javac編譯器使用的,可以限定集合中的輸入類(lèi)型,讓編譯器擋住源程序中的非法輸入,編譯器編譯帶類(lèi)型說(shuō)明的集合時(shí)會(huì)去除掉“類(lèi)型”信息,使程序運(yùn)行效率不受影響,對(duì)于參數(shù)化的泛型類(lèi)型,getClass()方法的返回值和原始類(lèi)型完全一樣。由于編譯生成的字節(jié)碼會(huì)去掉泛型的類(lèi)型信息,只要能跳過(guò)編譯器,就可以往某個(gè)泛型集合中加入其它類(lèi)型的數(shù)據(jù),例如,用反射得到集合,再調(diào)用其add方法即可。

泛型引用和創(chuàng)建兩端,給出的泛型變量必須相同

泛型類(lèi)

A<T>
Class<T> type

泛型類(lèi)中使用泛型

  • 成員類(lèi)型
  • 返回值和參數(shù)類(lèi)型
  • 局部變量的引用上
class A<T> {
    private T bean;//泛型可在成員變量上使用
    public T fun(T t) {}//泛型可以在類(lèi)中的方法上(返回值和參數(shù)類(lèi)型)使用!
    public void fun2() {//泛型還可以在局部變量的引用類(lèi)型上使用
        T b = ...
        new T();//不行的!
    }
}

泛型方法

public <T> T add(T x, T y){ 
}

泛型方法與泛型類(lèi)沒(méi)有什么關(guān)系,泛型方法不一定非要在泛型類(lèi)中!

泛型的繼承和實(shí)現(xiàn)

class A<T> {
}

// AA不是泛型類(lèi),只是它爸爸是泛型類(lèi)!
class AA extends A<String> {
  
} 

繼承泛型類(lèi)

  • 子類(lèi)不是泛型類(lèi):需要給父類(lèi)傳遞類(lèi)型常量

當(dāng)給父類(lèi)傳遞的類(lèi)型常量為String時(shí),那么在父類(lèi)中所有T都會(huì)被String替換!

class AA1 extends A<String> {
  
}
  • 子類(lèi)是泛型類(lèi):可以給父類(lèi)傳遞類(lèi)型常量,也可以傳遞類(lèi)型變量
class AA3<E> extends A<E> {
  
}

通配符

  • 無(wú)限通配符<?>
  • 向下通配符<? extends T>
  • 向上通配符<? super T>

類(lèi)型推斷

  • 通過(guò)反射的方式獲取泛型的實(shí)際類(lèi)型
  • 泛型只能是引用類(lèi)型,不能是基本數(shù)據(jù)類(lèi)型

泛型擦除

泛型會(huì)在編譯時(shí)擦除,List<String>和List<User>這兩個(gè)的字節(jié)碼文件那一個(gè)都是List.class

泛型封裝

本節(jié)內(nèi)容原文鏈接:http://www.lxweimin.com/p/d62c2be60617

你真的會(huì)用Gson嗎?Gson使用指南(一) 的第三節(jié)我介紹了在Gson中如何使用泛型來(lái)簡(jiǎn)化我們的類(lèi)設(shè)計(jì),但隨之而來(lái)引入了一個(gè)新的問(wèn)題:封裝。不知道各位有沒(méi)有想過(guò)這樣一個(gè)問(wèn)題:每次都要用 new TypeToken<XXX>(){}; 好麻煩,有沒(méi)有更好的辦法?

有更好的辦法么?當(dāng)然有!相信也有不少人自己作了嘗試,只是有人歡喜有人愁了,不過(guò)沒(méi)關(guān)系,今天我們就來(lái)解決這個(gè)問(wèn)題。

約定

1、本文涉及到的json格式

// data 為 object 的情況
{"code":"0","message":"success","data":{}}
// data 為 array 的情況
{"code":"0","message":"success","data":[]}

2、假定第一種的對(duì)應(yīng)的Java類(lèi)型為 Result<XXX> ,第二種為 Result<List<XXX>>

為何封裝,如何封裝

1. 為何封裝

  • 寫(xiě)new TypeToken<XXX>(){} 麻煩,IDE格式化后還不好看
  • 不同的地方每進(jìn)行一次 new TypeToken<XXX>(){} 操作都會(huì)生成一個(gè)新的類(lèi)
  • 對(duì)于任意類(lèi)XXX都只有兩種情況new TypeToken<Result<XXX>>(){}new TypeToken<Result<List<XXX>>>(){}
  • 方便統(tǒng)一管理

2. 如何封裝

從上面的我們可以知道,最簡(jiǎn)單的方法就是提供兩個(gè)方法分別對(duì)應(yīng)data為Array和Object的情況并接收一個(gè)參數(shù),即告知XXX的類(lèi)型,自動(dòng)將完成new TypeToken<XXX>(){}new TypeToken<Result<List<XXX>>>(){}的過(guò)程。

方法原型:

// 處理 data 為 object 的情況
public static <T> Result<T> fromJsonObject(Reader reader, Class<T> clazz) {}
// 處理 data 為 array 的情況
public static <T> Result<List<T>> fromJsonArray(Reader reader, Class<T> clazz){}

為何失敗?

對(duì)于那些嘗試著封裝過(guò)的人可能都這么寫(xiě)過(guò):

public static <T> Result<List<T>> fromJsonArray(Reader reader) {
    Type type = new TypeToken<Result<List<T>>>(){}.getType();
    return GSON.fromJson(reader, type);
}

當(dāng)然上面的寫(xiě)法肯定是沒(méi)有辦法完成的,雖然代碼不會(huì)報(bào)錯(cuò),但運(yùn)行結(jié)果肯定是不對(duì)的,因?yàn)檫@里的T 其實(shí)是一個(gè) TypeVariable,他在運(yùn)行時(shí)并不會(huì)變成我們想要的XXX,所以通過(guò)TypeToken 得到的 泛型信息只是 "Result<List<T>>"

如何解決?

既然TypeToken的作用是用于獲取泛型的類(lèi),返回的類(lèi)型為Type,真正的泛型信息就是放在這個(gè)Type里面,既然用TypeToken生成會(huì)有問(wèn)題,那我們自己生成Type就行了嘛。

Type是Java中所有類(lèi)型的父接口,在1.8以前是一個(gè)空接口,自1.8起多了個(gè)getTypeName()方法,下面有ParameterizedTypeGenericArrayTypeWildcardTypeTypeVariable 幾個(gè)接口,以及Class類(lèi)。這幾個(gè)接口在本次封裝過(guò)程中只會(huì)用到 ParameterizedType ,所以簡(jiǎn)單說(shuō)一下:

ParameterizedType 簡(jiǎn)單說(shuō)來(lái)就是形如“ 類(lèi)型<> ”的類(lèi)型,如:Map<String,User>。下面就以 Map<String,User> 為例講一下里面各個(gè)方法的作用。

public interface ParameterizedType extends Type {
     // 返回Map<String,User>里的String和User,所以這里返回[String.class,User.clas]
    Type[] getActualTypeArguments(); 
    // Map<String,User>里的Map,所以返回值是Map.class
    Type getRawType();
    // 用于這個(gè)泛型上中包含了內(nèi)部類(lèi)的情況,一般返回null
    Type getOwnerType(); 
}

所以,知道了這里需要的泛型是怎么回事,一切都好說(shuō)了,下面我們來(lái)完成之前留下的空方法。

1. 實(shí)現(xiàn)一個(gè)簡(jiǎn)易的 ParameterizedType

public class ParameterizedTypeImpl implements ParameterizedType {
    private final Class raw;
    private final Type[] args;
    public ParameterizedTypeImpl(Class raw, Type[] args) {
        this.raw = raw;
        this.args = args != null ? args : new Type[0];
    }
    @Override
    public Type[] getActualTypeArguments() {
        return args;
    }
    @Override
    public Type getRawType() {
        return raw;
    }
    @Override
    public Type getOwnerType() {return null;}
}

2. 生成Gson需要的泛型

2.1 解析data是object的情況
public static <T> Result<T> fromJsonObject(Reader reader, Class<T> clazz) {
    Type type = new ParameterizedTypeImpl(Result.class, new Class[]{clazz});
    return GSON.fromJson(reader, type);
}
2.2 解析data是array的情況

是Array的情況要比是Object的情況多那么一步。

public static <T> Result<List<T>> fromJsonArray(Reader reader, Class<T> clazz) {
    // 生成List<T> 中的 List<T>
    Type listType = new ParameterizedTypeImpl(List.class, new Class[]{clazz});
    // 根據(jù)List<T>生成完整的Result<List<T>>
    Type type = new ParameterizedTypeImpl(Result.class, new Type[]{listType});
    return GSON.fromJson(reader, type);
}

本次代碼較少,不提供源碼

雖然這篇博客是以Gson為例,但從上面的內(nèi)容可以看出實(shí)際上和Gson關(guān)系不大,主要的內(nèi)容還是Java的泛型基礎(chǔ),所以這種封裝的方法同樣適用于其它的框架。

最后借這次機(jī)會(huì)給安利一個(gè)簡(jiǎn)易的泛型生成庫(kù) TypeBuilder ,其最初實(shí)現(xiàn)的目的就是讓大家快速的生成泛型信息,同時(shí)也會(huì)作一些參數(shù)檢查,保證正確性。

用上面的代碼給大家舉個(gè)例子

public static <T> Result<List<T>> fromJsonArray(Reader reader, Class<T> clazz) {
    Type type = TypeBuilder
            .newInstance(Result.class)
            .beginSubType(List.class)
            .addTypeParam(clazz)
            .endSubType()
            .build();
    return GSON.fromJson(reader, type);
}

public static <T> Result<T> fromJsonObject(Reader reader, Class<T> clazz) {
    Type type = TypeBuilder
            .newInstance(Result.class)
            .addTypeParam(clazz)
            .build();
    return GSON.fromJson(reader, type);
}

Type

Type 是 Java 編程語(yǔ)言中所有類(lèi)型的公共高級(jí)接口。它們包括原始類(lèi)型、參數(shù)化類(lèi)型、數(shù)組類(lèi)型、類(lèi)型變量和基本類(lèi)型

ParameterizedType

ParameterizedType 表示參數(shù)化類(lèi)型,如 Collection<String>

方法 說(shuō)明
Type[ ] getActualTypeArguments() 獲取真實(shí)參數(shù)
public abstract class BaseProtocol<T> {
    ...
    /**泛型解析*/
    protected T parsejson(String jsonString){
        ParameterizedType genericSuperclass = (ParameterizedType) getClass().getGenericSuperclass();
        Type[] args = genericSuperclass.getActualTypeArguments();
        Type type = args[0];
        return GsonUtil.changeGsonToBean(jsonString,type);
    }
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,001評(píng)論 6 537
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,786評(píng)論 3 423
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人,你說(shuō)我怎么就攤上這事。” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 176,986評(píng)論 0 381
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 63,204評(píng)論 1 315
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 71,964評(píng)論 6 410
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 55,354評(píng)論 1 324
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,410評(píng)論 3 444
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 42,554評(píng)論 0 289
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,106評(píng)論 1 335
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,918評(píng)論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 43,093評(píng)論 1 371
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,648評(píng)論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,342評(píng)論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 34,755評(píng)論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 36,009評(píng)論 1 289
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 51,839評(píng)論 3 395
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 48,107評(píng)論 2 375

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