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开发相关知识,同时还分享技术人成长历程,包括我的总结,职场经验,面试经验等,但愿能让你少走一点弯路。

相关文章
相关标签/搜索