29.Android架構-註解與反射

註解的作用或者意義是什麼?

註解本身沒有任何意義,單獨的註解就是一種註釋,他需要結合其他如反射、插樁等技術纔有意義。

Java 註解(Annotation)又稱 Java 標註,是 JDK1.5 引入的一種註釋機制。是元數據的一種形式,提供有關於程序但不屬於程序本身的數據。註解對它們註解的代碼的操作沒有直接影響。

註解保留級別@Retention

RetentionPolicy.SOURCE
標記的註解僅保留在源碼級別中,並被編譯器忽略。

RetentionPolicy.CLASS
標記的註解在編譯時由編譯器保留,但 Java 虛擬機(JVM)會忽略。

RetentionPolicy.RUNTIME
標記的註解由 JVM 保留,因此運行時環境可以使用它。

SOURCE < CLASS < RUNTIME,即CLASS包含了SOURCE,RUNTIME包含SOURCE、CLASS

不同保留級別註解的應用場景

1.源碼級別 ( APT技術(Anotation Processor Tools) )

在編譯期能夠獲取註解與註解聲明的類包括類中所有成員信息,一般用於生成額外的輔助類。源碼級別的註解,可提供給IDE語法檢查(@Target({ANNOTATION_TYPE}))、APT等場景使用如ButterKnife,在類中使用 SOURCE 級別的註解,其編譯之後的class中會被丟棄。

註解處理器是 javac 自帶的一個工
具,用來在編譯時期掃描處理註解信息。你可以爲某些註解註冊自己的註解處理器。 註冊的註解處理器由 javac調起,並將註解信息傳遞給註解處理器進行處理。註解處理器是對註解應用最爲廣泛的場景。在Glide、EventBus3、Butterknifer、Tinker、ARouter等等常用框架中都有註解處理器的身影。但是你可能會發現,這些框架中對註解的定義並不是 SOURCE 級別,更多的是 CLASS 級別,別忘了:CLASS包含了SOURCE,RUNTIME包含SOURCE、CLASS。

2.字節碼級別 (字節碼增強技術)

在編譯出Class後,通過修改Class數據以實現修改代碼邏輯目的。對於是否需要修改的區分或者修改爲不同邏輯的判斷可以使用註解。如插樁
定義爲 CLASS 的註解,會保留在class文件中,但是會被虛擬機忽略(即無法在運行期反射獲取註解)。此時完全符合此種註解的應用場景爲字節碼操作。如:AspectJ、熱修復Roubust中應用此場景。所謂字節碼操作即爲,直接修改字節碼Class文件以達到修改代碼執行邏輯的目的。

3.運行時級別 (反射技術)

註解保留至運行期,意味着我們能夠在運行期間結合反射技術獲取註解中的所有信息。
在程序運行期間,通過反射技術動態獲取註解與其元素,從而完成不同的邏輯判定。如一些比較古老的框架在運行時通過反射進行view的findViewById操作,或者Gson中也使用到了這個能力

反射

反射就是在運行狀態中,對於任意一個類,都能夠知道這個類的所有屬性和方法;對於任意一個對象,都能夠調用它的任意方法和屬性;並且能改變它的屬性。是Java被視爲動態語言的關鍵。

反射如何獲取泛型的真實類型?

當我們對一個泛型類進行反射時,需要得到泛型中的真實數據類型,來完成如json反序列化的操作。此時需要通過 Type 體系來完成。 Type 接口包含了一個實現類(Class)和四個實現接口,他們分別是:

TypeVariable

泛型類型變量。可以泛型上下限等信息;

public class TestFanxing<K extends Comparable & Serializable, V> {

    K key;
    V value;

    public static void main(String[] args) throws Exception {
        // 獲取字段的類型
        Field fk = TestFanxing.class.getDeclaredField("key");
        Field fv = TestFanxing.class.getDeclaredField("value");
        TypeVariable keyType = (TypeVariable) fk.getGenericType();
        TypeVariable valueType = (TypeVariable) fv.getGenericType();
        // getName 方法
        System.out.println(keyType.getName());
        // K
        System.out.println(valueType.getName());
        // V // getGenericDeclaration 方法
        System.out.println(keyType.getGenericDeclaration());
        // class com.test.TestType
        System.out.println(valueType.getGenericDeclaration());
        // class com.test.TestType // getBounds 方法
        System.out.println("K 的上界:");
        // 有兩個
        for (Type type : keyType.getBounds()) {
            // interface java.lang.Comparable
            System.out.println(type);
            // interface java.io.Serializable
        }
        System.out.println("V 的上界:");
        // 沒明確聲明上界的, 默認上界是 Object
        for (Type type : valueType.getBounds()) {
            // class java.lang.Object
            System.out.println(type);
        }
    }
}

打印結果:
K
V
class TestFanxing
class TestFanxing
K 的上界:
interface java.lang.Comparable
interface java.io.Serializable
V 的上界:
class java.lang.Object
ParameterizedType

具體的泛型類型,可以獲得元數據中泛型簽名類型(泛型真實類型)

public class TestFanxing<K extends Comparable & Serializable, V> {
    Map<String, String> map;
    public static void main(String[] args) throws Exception {
        Field f = TestFanxing.class.getDeclaredField("map");
        System.out.println(f.getGenericType());
        // java.util.Map<java.lang.String, java.lang.String>
        ParameterizedType pType = (ParameterizedType) f.getGenericType();
        System.out.println(pType.getRawType());
        // interface java.util.Map
        for (Type type : pType.getActualTypeArguments()) {
            System.out.println(type);
            // 打印兩遍: class java.lang.String
        }
    }
}

java.util.Map<java.lang.String, java.lang.String>
interface java.util.Map
class java.lang.String
class java.lang.String
GenericArrayType

當需要描述的類型是泛型類的數組時,比如List[],Map[],此接口會作爲Type的實現。

public class TestFanxing<K extends Comparable & Serializable, V> {
    List<String>[] lists;
    public static void main(String[] args) throws Exception {
        Field f = TestFanxing.class.getDeclaredField("lists");
        GenericArrayType genericType = (GenericArrayType) f.getGenericType();
        System.out.println(genericType.getGenericComponentType());
    }
}

java.util.List<java.lang.String>
WildcardType

通配符泛型,獲得上下限信息;

public class TestFanxing<K extends Comparable & Serializable, V> {

    private List<? extends Number> a;
    private List<? super String> b;

    public static void main(String[] args) throws Exception {
        Field fieldA = TestFanxing.class.getDeclaredField("a");
        Field fieldB = TestFanxing.class.getDeclaredField("b");
        //先拿到範型類型
        ParameterizedType pTypeA = (ParameterizedType) fieldA.getGenericType();
        ParameterizedType pTypeB = (ParameterizedType) fieldB.getGenericType();
        //再從範型裏拿到通配符類型
        WildcardType wTypeA = (WildcardType) pTypeA.getActualTypeArguments()[0];
        WildcardType wTypeB = (WildcardType) pTypeB.getActualTypeArguments()[0];
        //方法測試
        System.out.println(wTypeA.getUpperBounds()[0]);
        //class java.lang.Number
        System.out.println(wTypeB.getLowerBounds()[0]);
        //class java.lang.String 看看通配符類型到底是什麼, 打印結果爲: ? extends java.lang.Number
        System.out.println(wTypeA);
    }
}

class java.lang.Number
class java.lang.String
? extends java.lang.Number

Gson反序列化

static class Response<T> {
    T data;
    int code;
    String message;

    @Override
    public String toString() {
        return "Response{" + "data=" + data + ", code=" + code + ", message='" + message + '\'' + '}';
    }

    public Response(T data, int code, String message) {
        this.data = data;
        this.code = code;
        this.message = message;
    }
}

static class Data {
    String result;

    public Data(String result) {
        this.result = result;
    }

    @Override
    public String toString() {
        return "Data{" + "result=" + result + '}';
    }
}

    public static void main(String[] args) {
        Response<Data> dataResponse = new Response(new Data("數據"), 1, "成功");
        Gson gson = new Gson();
        String json = gson.toJson(dataResponse);
        System.out.println(json);
        //爲什麼TypeToken要定義爲抽象類? 
        Response<Data> resp = gson.fromJson(json, new TypeToken<Response<Data>>() {
        }.getType());
        System.out.println(resp.data.result);
    }

在進行GSON反序列化時,存在泛型時,可以藉助 TypeToken 獲取Type以完成泛型的反序列化。但是爲什麼
TypeToken 要被定義爲抽象類呢?
因爲只有定義爲抽象類或者接口,這樣在使用時,需要創建對應的實現類,此時確定泛型類型,編譯才能夠將泛型
signature信息記錄到Class元數據中。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章