java Type 詳解

爲何要寫這一系列的博客呢?java

由於在 Android 開發的過程當中, 泛型,反射,註解這些知識進場會用到,幾乎全部的框架至少都會用到上面的一兩種知識,如 Gson 就用到泛型,反射,註解,Retrofit 也用到泛型,反射,註解 。學好這些知識對咱們進階很是重要,尤爲是閱讀開源框架源碼或者本身開發開源框架。程序員

java Type 詳解面試

java 反射機制詳解數組

註解使用入門(一)bash

Android 自定義編譯時註解1 - 簡單的例子微信

Android 編譯時註解 —— 語法詳解框架

帶你讀懂 ButterKnife 的源碼學習

前言

錯誤可分爲兩種:編譯時錯誤與運行時錯誤。編譯時錯誤在編譯時能夠發現並排除,而運行時錯誤具備很大的不肯定性,在程序運行時才能發現,形成的後果多是災難性的。測試

泛型的引入使得一部分錯誤能夠提早到編譯時期發現,極大地加強了代碼的健壯性。可是咱們知道 java 泛型在運行的時候是會進行泛型擦除的,那咱們要怎樣獲得在編譯時期泛型的信息呢?java 爲咱們提供了 Type 接口,使用它,咱們能夠獲得這些信息。ui

不知道什麼是泛型擦除的同窗能夠看一下

類型擦除是指泛型在運行的時候會去除泛型的類型信息。java 中,泛型主要是在編譯層次來實現的,在生成的字節碼即 class 文件是不包括泛型的 類型信息的。 即 List, List ,List 雖然在編譯時候是不一樣的,可是在編譯完成後,在class 文件 中都只會把他們看成 List 來對待。


Type 接口簡介

類 UML 圖以下

簡單來講:Type是全部類型的父接口, 如原始類型(raw types 對應 Class)、 參數化類型(parameterized types 對應 ParameterizedType)、 數組類型(array types 對應 GenericArrayType)、 類型變量(type variables 對應 TypeVariable )和基本(原生)類型(primitive types 對應 Class),。

子接口有 ParameterizedType, TypeVariable, GenericArrayType, WildcardType, 實現類有Class。


ParameterizedType (參數化類型)

官方文檔的說明是這樣的

ParameterizedType represents a parameterized type such as Collection

須要注意的是,並不僅是 Collection 纔是 parameterized,任何相似於 ClassName 這樣的類型都是 ParameterizedType ,好比下面的這些都是 parameterizedType.

Map<String, Person> map;
	Set<String> set1;
	Class<?> clz;
	Holder<String> holder;
	List<String> list;

	static class Holder<V>{
		
	}
複製代碼

而相似於這樣的 ClassName 不是 ParameterizedType.

Set set;
List aList;
複製代碼

ParameterizedType 的幾個主要方法

  • Type[] getActualTypeArguments();
  • Type getRawType();
  • Type getOwnerType();

Type[] getActualTypeArguments(); 返回 這個 Type 類型的參數的實際類型數組。 如 Map<String,Person> map 這個 ParameterizedType 返回的是 String 類,Person 類的全限定類名的 Type Array。

Type getRawType() 返回的是當前這個 ParameterizedType 的類型。 如 Map<String,Person> map 這個 ParameterizedType 返回的是 Map 類的全限定類名的 Type Array。

Type getOwnerType();

Returns a {@code Type} object representing the type that this type is a member of.

這個比較少用到。返回的是這個 ParameterizedType 所在的類的 Type (注意當前的 ParameterizedType 必須屬於所在類的 member)。解釋起來有點彆扭,仍是直接用代碼說明吧。 好比 Map<String,Person> map 這個 ParameterizedType 的 getOwnerType() 爲 null,而 Map.Entry<String, String>entry 的 getOwnerType() 爲 Map 所屬於的 Type。

說了這麼多,下面咱們一塊兒來看一下例子,加深印象。

public class ParameterizedTypeBean {
	// 下面的 field 的 Type 屬於 ParameterizedType
	Map<String, Person> map;
	Set<String> set1;
	Class<?> clz;
	Holder<String> holder;
	List<String> list;
	// Map<String,Person> map 這個 ParameterizedType 的 getOwnerType() 爲 null,
	// 而 Map.Entry<String, String> entry 的 getOwnerType() 爲 Map 所屬於的 Type。
	Map.Entry<String, String> entry;
	// 下面的 field 的 Type 不屬於ParameterizedType
	String str;
	Integer i;
	Set set;
	List aList;

	static class Holder<V> {

	}
}
複製代碼
public class TestHelper {

	public static void testParameterizedType() {
		Field f = null;
		try {
			Field[] fields = ParameterizedTypeBean.class.getDeclaredFields();
            // 打印出全部的 Field 的 TYpe 是否屬於 ParameterizedType
			for (int i = 0; i < fields.length; i++) {
				f = fields[i];
				PrintUtils.print(f.getName()
						+ " getGenericType() instanceof ParameterizedType "
						+ (f.getGenericType() instanceof ParameterizedType));
			}
			getParameterizedTypeMes("map" );
			getParameterizedTypeMes("entry" );
			

		} catch (NoSuchFieldException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (SecurityException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

	}

	private static void getParameterizedTypeMes(String fieldName) throws NoSuchFieldException {
		Field f;
		f = ParameterizedTypeBean.class.getDeclaredField(fieldName);
		f.setAccessible(true);
		PrintUtils.print(f.getGenericType());
		boolean b=f.getGenericType() instanceof ParameterizedType;
		PrintUtils.print(b);
		if(b){
			ParameterizedType pType = (ParameterizedType) f.getGenericType();
			PrintUtils.print(pType.getRawType());
			for (Type type : pType.getActualTypeArguments()) {
				PrintUtils.print(type);
			}
			PrintUtils.print(pType.getOwnerType()); // null
		}
	}
}
複製代碼

print:map getGenericType() instanceof ParameterizedType true

print:set1 getGenericType() instanceof ParameterizedType true

print:clz getGenericType() instanceof ParameterizedType true

print:holder getGenericType() instanceof ParameterizedType true

print:list getGenericType() instanceof ParameterizedType true

print:str getGenericType() instanceof ParameterizedType false

print:i getGenericType() instanceof ParameterizedType false

print:set getGenericType() instanceof ParameterizedType false

print:aList getGenericType() instanceof ParameterizedType false

print:entry getGenericType() instanceof ParameterizedType true

print:java.util.Map<java.lang.String, com.xujun.gennericity.Person>

print:true

print:interface java.util.Map

print:class java.lang.String

print:class com.xujun.gennericity.Person

print:null

print:java.util.Map.java.util.Map$Entry<java.lang.String, java.lang.String>

print:true

print:interface java.util.Map$Entry

print:class java.lang.String

print:class java.lang.String

print:interface java.util.Map


TypeVariable 變量

好比 public class TypeVariableBean<K extends InputStream & Serializable, V> ,K ,V 都是屬於類型變量。

主要方法

  • Type[] getBounds(); 獲得上邊界的 Type數組,如 K 的上邊界數組是 InputStream 和 Serializable。 V 沒有指定的話,上邊界是 Object
  • D getGenericDeclaration(); 返回的是聲明這個 Type 所在的類 的 Type
  • String getName(); 返回的是這個 type variable 的名稱
public class TypeVariableBean<K extends InputStream & Closeable, V> {
	// K 的上邊界是 InputStream
	K key;
	// 沒有指定的話 ,V 的 上邊界 屬於 Object
	V value;
	// 不屬於 TypeTypeVariable
	V[] values;
	String str;
	List<K> kList;

}
複製代碼
TypeVariableBean bean = new TypeVariableBean<FileInputStream, String>();
fk = TypeVariableBean.class.getDeclaredField("key");
eyType = (TypeVariable) fk.getGenericType();
System.out.println(keyType.getName());System.out.println(keyType.getGenericDeclaration());
複製代碼

執行上述代碼,將能夠看到以下的效果

K

class com.xujun.gennericity.beans.TypeVariableBean


GenericArrayType

represents an array type whose component type is either a parameterized type or a type variable.

簡單來講就是:範型數組,組成數組的元素中有範型則實現了該接口; 它的組成元素是 ParameterizedType 或 TypeVariable 類型

// 屬於 GenericArrayType
List<String>[] pTypeArray;
// 屬於 GenericArrayType
T[] vTypeArray;
// 不屬於 GenericArrayType
List<String> list;
// 不屬於 GenericArrayType
String[] strings;
// 不屬於 GenericArrayType
Person[] ints;
複製代碼

下面咱們一塊兒來看一下例子

public class GenericArrayTypeBean<T> {
	
	public void test(List<String>[] pTypeArray, T[] vTypeArray,
			 List<String> list, String[] strings, Person[] ints) {
	    }
}
複製代碼
public static void testGenericArrayType() {
		Method method = GenericArrayTypeBean.class.getDeclaredMethods()[0];
		System.out.println(method);
		// public void test(List<String>[] pTypeArray, T[]
		// vTypeArray,List<String> list, String[] strings, Person[] ints)
		Type[] types = method.getGenericParameterTypes(); // 這是 Method 中的方法
		for (Type type : types) {
			System.out.println(type instanceof GenericArrayType);// 依次輸出truetruefalsefalsefalse
		}
	}
複製代碼

輸出結果

public void com.xujun.gennericity.beans.GenericArrayTypeBean.test(java.util.List[],java.lang.Object[],java.util.List,java.lang.String[],com.xujun.gennericity.Person[])

true

true

false

false

false


WildcardType 通配符的類型

{@code ?}, {@code ? extends Number}, or {@code ? super Integer} 這些類型 都屬於 WildcardType

extends 用來指定上邊界,沒有指定的話上邊界默認是 Object, super 用來指定下邊界,沒有指定的話爲 null。

幾個主要方法介紹

  • Type[] getLowerBounds() 獲得上邊界 Type 的數組
  • Type[] getUpperBounds() 獲得下邊界 Type 的數組

下面一塊兒來看一下例子。

public class WildcardTypeBean {
	private List<? extends Number> a;  // a沒有下界,
//	沒有指定的話,上邊界默認是 Object ,下邊界是 	String						
	private List<? super String> b; 
	
	private List<String> c;
	
	private Class<?> aClass;
}
複製代碼
public static void testWildCardType() {
		try {
			Field[] fields = WildcardTypeBean.class.getDeclaredFields();
			for (int i = 0; i < fields.length; i++) {
				Field field = fields[i];
				Type type = field.getGenericType();
				String nameString = field.getName();
				PrintUtils.print("下面開始打印" + nameString + "是否具備通配符");
				if (!(type instanceof ParameterizedType)) {
					PrintUtils.print("---------------------------");
					continue;
				}
				ParameterizedType parameterizedType = (ParameterizedType) type;
				type = parameterizedType.getActualTypeArguments()[0];
				if (!(type instanceof WildcardType)) {
					PrintUtils.print("---------------------------");
					continue;
				}
				WildcardType wildcardType = (WildcardType) type;
				Type[] lowerTypes = wildcardType.getLowerBounds();
				if (lowerTypes != null) {
					PrintUtils.print("下邊界");
					PrintUtils.printTypeArr(lowerTypes);
				}
				Type[] upTypes = wildcardType.getUpperBounds();
				if (upTypes != null) {
					PrintUtils.print("上邊界");
					PrintUtils.printTypeArr(upTypes);
				}
				PrintUtils.print("---------------------------");

			}
			Field fieldA = WildcardTypeBean.class.getDeclaredField("a");
			Field fieldB = WildcardTypeBean.class.getDeclaredField("b");
			// 先拿到範型類型
			PrintUtils.print(fieldA.getGenericType() instanceof ParameterizedType);
			PrintUtils.print(fieldB.getGenericType() instanceof ParameterizedType);
			ParameterizedType pTypeA = (ParameterizedType) fieldA.getGenericType();
			ParameterizedType pTypeB = (ParameterizedType) fieldB.getGenericType();
			// 再從範型裏拿到通配符類型
			PrintUtils.print(pTypeA.getActualTypeArguments()[0] instanceof WildcardType);
			PrintUtils.print(pTypeB.getActualTypeArguments()[0] instanceof WildcardType);
			WildcardType wTypeA = (WildcardType) pTypeA.getActualTypeArguments()[0];
			WildcardType wTypeB = (WildcardType) pTypeB.getActualTypeArguments()[0];
			// 方法測試
			System.out.println(wTypeA.getUpperBounds()[0]); 
			System.out.println(wTypeB.getLowerBounds()[0]); 
			// 看看通配符類型究竟是什麼, 打印結果爲: ? extends java.lang.Number
			System.out.println(wTypeA);
		} catch (NoSuchFieldException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (SecurityException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

	}
複製代碼

java Type 總結

Type及其子接口的來歷

  • 泛型出現以前的類型

沒有泛型的時候,只有原始類型。此時,全部的原始類型都經過字節碼文件類Class類進行抽象。Class類的一個具體對象就表明一個指定的原始類型。

  • 泛型出現以後的類型

泛型出現以後,擴充了數據類型。從只有原始類型擴充了參數化類型、類型變量類型、限定符類型 、泛型數組類型。

爲何會產生泛型擦除的緣由

咱們知道在 jdk 1.5 之前的時候,是沒有 泛型的。在 jdk 1.5 的時候,才引入了泛型。若是真的在動態運行的時候加入泛型,涉及到 JVM 命令的修改,這無疑是很是致命的。所以折中採起了這樣的策略,在編譯的時候進行檢查,在運行的時候進行擦除,也是咱們說的泛型擦除。 同時這也說明一點,在設計框架的時候,框架的健壯性和靈活性很是重要。

爲何要學習 Type

咱們知道如今的框架都會使用泛型,掌握 Type 有利於咱們讀懂它們的源碼,或者本身動手打造框架。如 Android 的經常使用開源框架 Gson ,Retrofit等。

題外話

最近更新博客的頻率有點低,主要是由於惰性吧。天天實習完之後,有點累,就不太想寫博客了。我如今也不知道我能堅持到何時,順其天然吧。PS,真的愈來愈佩服那些堅持寫博客的人,大家是最棒的。

文章首發地址 CSDN

Demo下載地址

相關博客推薦

java Type 詳解

java 反射機制詳解

註解使用入門(一)

Android 自定義編譯時註解1 - 簡單的例子

Android 編譯時註解 —— 語法詳解

帶你讀懂 ButterKnife 的源碼

掃一掃,歡迎關注個人微信公衆號 stormjun94(徐公碼字), 目前是一名程序員,不只分享 Android開發相關知識,同時還分享技術人成長曆程,包括我的總結,職場經驗,面試經驗等,但願能讓你少走一點彎路。

相關文章
相關標籤/搜索