Java在加入泛型之後,僅僅Class已經不足以描述資料的型別了,比如List<String>型別的資料,其Class的型別為List.class,但是其型別包含了泛型引數,所以java引入了Type型別來描述泛型型別。除了泛型型別,還有陣列格式的資料,其型別也包含兩部分,一部分是陣列物件本身的class,另外一部分是陣列中資料的型別。本文會詳細介紹JavaType中的各種型別,分析這些型別的使用方法。

Type介紹

Type是Java 程式語言中所有型別的公共高階介面,也就是Java中所有"型別"的介面。官方原話定義如下

> 官方文件:Type is the common superinterface for all types in the Java programming language. These include raw types, parameterized types, array types, type variables and primitive types.

這樣的官方描述有點難懂,此處我畫個圖解釋一下。Type其實是和泛型一起出現的,可以說Type就是為了支援泛型。

  • 泛型出現之前,我們可以通過Class來確認一個物件的型別,比如ClassA A,那麼A的型別就是ClassA;
  • 泛型出現之後,顯然不能通過Class唯一確認一個物件的型別,比如List<ClassA> A,A的Class是List,但是A的型別顯然不僅僅是List,它是由Class型別的List + TypeVariables的ClassA聯合確認的一個Type。

> A type variable is an unqualified identifier used as a type in class, interface, method, and constructor bodies.

Type的型別

Type可以分為兩大類:包含TypeVariables和不包含TypeVariables的型別:

  • 不包含TypeVariable:包含基本資料型別(int, long等),基本Class(如Object,不包含泛型的類);

  • 包含TypeVariable,按照包含的TypeVariable又分為以下幾類:

    • ParameterizedType: 表示一種引數化的型別,如List<String>,泛型的引數已經指定;
    • GenericArrayType: 表示一種元素型別是引數化型別或者型別變數的陣列型別,如List<String>[][];
    • WildcardType: 代表一種萬用字元型別表示式,比如List<?>, List<? extends ClassA>, List<? super Object>。

繼續介紹Type之前,需要先介紹一下java的泛型機制:

泛型是Java SE 1.5的新特性,泛型的本質是引數化型別,也就是說所操作的資料型別被指定為一個引數。這種引數型別可以用在類、介面和方法的建立中,分別稱為泛型類、泛型介面、泛型方法。 Java語言引入泛型的好處是安全簡單。泛型的好處是在編譯的時候檢查型別安全,並且所有的強制轉換都是自動和隱式的,以提高程式碼的重用率。

泛型資訊只存在於程式碼編譯階段,在進入 JVM 之前,與泛型相關的資訊會被擦除掉,專業術語叫做型別擦除。

引數化型別ParameterizedType

引數化型別的寫法如下:C<T1,...,Tn>,其中C是Class型別,<T1,...,Tn> 是Type,先列幾個引數化型別的合法定義:

Seq\<String\>
Seq\<Seq\<String\>\>
Seq\<String\>.Zipper\<Integer\>
Pair\<String,Integer\>

ParameterizedType型別的介面方法介紹:

返回值 方法名稱 描述資訊
Type[] getActualTypeArguments() 引數化型別中的TypeVariable引數型別,如List<String> 返回 String.class, List<List<<String>> 返回List<<String>
Type getOwnerType() 獲取當前Type所屬的Type,比如對於O<T>.I<S>中的I<S>型別,會返回 O<T>
Type getRawType() 獲取當前Type的Class,如List<String> 返回 List.class

>ParameterizedType represents a parameterized type such as Collection<String>.A parameterized type is created the first time it is needed by a reflective method, as specified in this package. When a parameterized type p is created, the generic type declaration that p instantiates is resolved, and all type arguments of p are created recursively. See TypeVariable for details on the creation process for type variables. Repeated creation of a parameterized type has no effect. Instances of classes that implement this interface must implement an equals() method that equates any two instances that share the same generic type declaration and have equal type parameters.

陣列型別GenericArrayType

陣列泛型型別的寫法如下:C<T>[],其中C是Class型別,<T> 是Type,先列幾個陣列泛型型別的合法定義:

List\<String\>[]
List\<Seq\<String\>\> [][]
返回值 方法名稱 描述資訊
Type getGenericComponentType() 陣列元素的型別,如List<String> []返回List<String>

注意:<>不能出現在陣列的初始化中,即new陣列之後不能出現<>,否則javac無法通過。但是作為引用變數或者方法的某個引數是完全可以的。不包含泛型的陣列本節不做介紹(如String[]),下文中會進行介紹。

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

萬用字元型別WildcardType

萬用字元型別何其字面意思相同,其泛型型別不再是一個具體的類,而是一個萬用字元表示式,表示式包含以下三種:"?","? extends Type", "? super Type",其中Type可以為WildcardType,GenericArrayType,ParameterizedType,Class.

>WildcardType represents a wildcard type expression, such as ?, ? extends Number, or ? super Integer.

WildcardType 介面的方法和介紹如下.

返回值 方法名稱 描述資訊
Type[] getLowerBounds() 返回通配Type的下限型別,現階段返回值的長度為1
Type[] getUpperBounds() 返回通配Type的上限型別,現階段返回值的長度為1

基本Class、基本資料型別和陣列

通過反射獲取基本的Class和基本資料型別此處就不詳細介紹了,接下來會重點介紹一下陣列型別。java的陣列型別由虛擬機器生成,虛擬機器生成的陣列型別的名稱一般類似於"class [[Ljava.lang.String;",注意其中的"[["表示是二維陣列。那麼如何獲取陣列中的元素型別呢? java.lang.Class包中提供了以下介面查詢:

返回值 方法名稱 描述資訊
Class<?> componentType() 如果型別是陣列型別,返回陣列中元素的型別,否則返回null

>componentType():Returns the component type of this Class, if it describes an array type, or null otherwise.

如何獲取欄位或引數的Type資訊

平時使用java程式的過程中,我們接觸到的最多的型別只有Class,像泛型型別和陣列型別,通常只有通過反射才能獲取到。

獲取欄位的泛型資訊

如下程式中,我們首先定義了一個自定義的類TestParameterizedType,只包含一個欄位List<String> field,然後我們在另外一個單測例項中嘗試通過反射獲取field的相關資訊。通過field.getType()我們獲取到了field的型別。通過field.getGenericType()我們獲取到了field的泛型資訊。

public class ReflectParameterizedTypeTest {

    public static class TestParameterizedType {
private List\<String\> field;
} @Test
public void testIntType() throws NoSuchFieldException {
Class\<?\> clazz = TestParameterizedType.class;
Field field = clazz.getDeclaredField("field"); // 此處獲取到欄位的實際Class型別
Class\<?\> clazzType = field.getType();
System.out.println("Field type: " + clazzType.getName()); // 此處獲取到欄位的泛型型別
Type genericType = field.getGenericType();
System.out.println("Field generic type: " + field.getGenericType().getTypeName());
}
}

獲取方法引數的泛型資訊

類似於欄位的獲取方式,方法可以通過Method.getGenericParameterTypes()獲取所有引數的泛型資訊。

獲取執行時變數的泛型資訊

不可能,具體原因參考java的泛型擦除原理。

本文大多數內容參考了java官方文件,點此直達JAVA15官方文件地址

我是御狐神,歡迎大家關注我的微信公眾號

本文最先發布至微信公眾號,版權所有,禁止轉載!