註解的作用或者意義是什麼?
註解本身沒有任何意義,單獨的註解就是一種註釋,他需要結合其他如反射、插樁等技術纔有意義。
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元數據中。