秒懂Java类型(Type)系统

版权申明】非商业目的注明出处可自由转载
博文地址:https://blog.csdn.net/ShuSheng0007/article/details/89520530
出自:shusheng007

相关系列文章:
秒懂Java泛型
秒懂Java反射

概述

Java 编程语言从Java 5以后就引入Type体系,应该是为了加入泛型而引入的。其存在于java.lang.reflect包下面,所以平时较少使用,但是理解Java类型体系是深入理解Java泛型的基础,也是一个高级Java开发者的必备素质。

Type 系统总览

Java Type 体系,始于Type接口,其是Java编程语言中所有类型的父接口,是对Java编程语言类型的一个抽象,源码如下所示:

public interface Type {
    //返回这个类型的描述,包括此类型的参数描述。
    default String getTypeName() {
        return toString();
    }
}

其有4个子接口GenericArrayTypeParameterizedTypeTypeVariableWildcardType和一个实现类Class, Java 的整个泛型体系就由他们支撑,下面我们分别看一下他们到底是个什么东东:

我们需要一个类和一个注解来作为讲解代码的基础。

示例类TypeTest 是一个泛型类,声明了两个泛型参数T 和 V,一个构造函数和一个泛型方法以及若干属性。如果你看不懂下面这个示例类,说明你对Java 泛型的了解还停留在初级阶段,你更需要这篇文章秒懂Java泛型,然后才是这篇,如若强行阅读,效果可能不佳。

/**
 * Created by Shusheng007 on 2019/4/25.
 * 泛型类,参数为T 和 V
 */
public class TypeTest<T, V extends @Custom Number & Serializable> {
    private Number number;
    public T t;
    public V v;
    public List<T> list = new ArrayList<>();
    public Map<String, T> map = new HashMap<>();

    public T[] tArray;
    public List<T>[] ltArray;

    public TypeTest testClass;
    public TypeTest<T, Integer> testClass2;

    public Map<? super String, ? extends Number> mapWithWildcard;

    //泛型构造函数,泛型参数为X
    public <X extends Number> TypeTest(X x, T t) {
        number = x;
        this.t = t;
    }

    //泛型方法,泛型参数为Y
    public <Y extends T> void method(Y y) {
        t = y;
    }
}

自定义注解 Custom:

@Retention(RetentionPolicy.RUNTIME)
@Target(value = {ANNOTATION_TYPE, CONSTRUCTOR, FIELD,
        METHOD, PACKAGE, PARAMETER, TYPE, TYPE_PARAMETER, TYPE_USE})
public @interface Custom {
}

下面的code 都是以上面的类为基础撰写的。

个人觉得我们应该先从TypeVariable作为切入点来进入Java Type的世界,因为它是其他类型的基础。

TypeVariable:

类型变量,例如List<T>中的T, Map<K,V>中的KV,我们的测试类class TypeTest<T, V extends @Custom Number & Serializable>中的TV

此接口源码如下:

interface TypeVariable<D extends GenericDeclaration> extends Type, AnnotatedElement {
    //返回此类型参数的上界列表,如果没有上界则放回Object. 例如  V extends @Custom Number & Serializable 这个类型参数,有两个上界,Number 和 Serializable 
	Type[] getBounds();
	//类型参数声明时的载体,例如 `class TypeTest<T, V extends @Custom Number & Serializable>` ,那么V 的载体就是TypeTest
	D getGenericDeclaration();
	String getName();
	//Java 1.8加入 AnnotatedType: 如果这个这个泛型参数类型的上界用注解标记了,我们可以通过它拿到相应的注解
	AnnotatedType[] getAnnotatedBounds();
}

从typeVariable的定义看到其也有一个泛型参数,要求需要是GenericDeclaration 的子类,

 //所有可以申明泛型参数的entities都必须实现这个接口
 public interface GenericDeclaration extends AnnotatedElement {     
    public TypeVariable<?>[] getTypeParameters();
}

我们从源码中看到,只有三个类实现了这个接口,分别是:

java.lang.reflect.Method,
java.lang.reflect.Constructor,
java.lang.Class

所以我们只能在类型(例如Class,Interface)、方法构造函数这三个地方声明泛型参数,其他地方只能使用。不明白这块的,请自行参考 秒懂Java泛型

翠花提醒到,二狗别哔哔了,千言万语不如一段源码,该上code了:

//****************************TypeVariable************************
Field v = TypeTest.class.getField("v");//用反射的方式获取属性 public V v; 
TypeVariable typeVariable = (TypeVariable) v.getGenericType();//获取属性类型
System.out.println("TypeVariable1:" + typeVariable);
System.out.println("TypeVariable2:" + Arrays.asList(typeVariable.getBounds()));//获取类型变量上界
System.out.println("TypeVariable3:" + typeVariable.getGenericDeclaration());//获取类型变量声明载体
//1.8 AnnotatedType: 如果这个这个泛型参数类型的上界用注解标记了,我们可以通过它拿到相应的注解
AnnotatedType[] annotatedTypes = typeVariable.getAnnotatedBounds();        
System.out.println("TypeVariable4:" + Arrays.asList(annotatedTypes) + " : " +
                                                                    Arrays.asList(annotatedTypes[0].getAnnotations()));
System.out.println("TypeVariable5:" + typeVariable.getName());

输出结果为:

TypeVariable1:V
TypeVariable2:[class java.lang.Number, interface java.io.Serializable]
TypeVariable3:class typeInfo.TypeTest
TypeVariable4:[sun.reflect.annotation.AnnotatedTypeFactory$AnnotatedTypeBaseImpl@511d50c0, sun.reflect.annotation.AnnotatedTypeFactory$AnnotatedTypeBaseImpl@60e53b93] : [@typeInfo.Custom()]
TypeVariable5:V

值得注意的是,我们通过annotatedTypes[0].getAnnotations()[0]获取到了类型参数V的上界Number 上的注解Custom(),这里需要重点理解一下。

ParameterizedType:

参数化类型,即带参数的类型,也可以说带<>的类型。例如List<String>, User<T> 等。

其源码如下:

interface ParameterizedType extends Type {
     //获取参数类型<>里面的那些值,例如Map<K,V> 那么就得到 [K,V]的一个数组
     Type[] getActualTypeArguments(); 
     //获取参数类型<>前面的值,例如例如Map<K,V> 那么就得到 Map
     Type getRawType();
     //获取其父类的类型,例如Map 有一个内部类Entry,  那么在Map.Entry<K,V> 上调用这个方法就可以获得 Map
     Type getOwnerType();
}

这个接口的3个方法已经在上面描述的很清楚了,接下来我实际操作一下。翠花,上code

//*********************************ParameterizedType**********************************************       
Field list = TypeTest.class.getField("list");
Type genericType1 = list.getGenericType();
System.out.println("参数类型1:" + genericType1.getTypeName()); //参数类型1:java.util.List<T>

Field map = TypeTest.class.getField("map");
Type genericType2 = map.getGenericType();
System.out.println("参数类型2:" + genericType2.getTypeName());//参数类型2:java.util.Map<java.lang.String, T>

if (genericType2 instanceof ParameterizedType) {
     ParameterizedType pType = (ParameterizedType) genericType2;
     Type[] types = pType.getActualTypeArguments();
     System.out.println("参数类型列表:" + Arrays.asList(types));//参数类型列表:[class java.lang.String, T]
     System.out.println("参数原始类型:" + pType.getRawType());//参数原始类型:interface java.util.Map
     System.out.println("参数父类类型:" + pType.getOwnerType());//参数父类类型:null,因为Map没有外部类,所以为null
}

输出:

参数类型1:java.util.List<T>
参数类型2:java.util.Map<java.lang.String, T>
参数类型列表:[class java.lang.String, T]
参数原始类型:interface java.util.Map
参数父类类型:null

上面的代码,先使用反射获取TypeTest类的List<T> listMap<String, T> map属性的类型,然后调用getGenericType()获取他们的声明类型,他们是ParameterizedType类型,然后调用里面的方法。

GenericArrayType

泛型数组类型,用来作为数组的泛型声明类型。例如List<T>[] ltArrayT[] tArray两个数组,其中List<T>[], 和T[]就是GenericArrayType`类型。

此接口的源码如下:

public interface GenericArrayType extends Type {
    //获取泛型类型数组的声明类型,即获取数组方括号 [] 前面的部分
    Type getGenericComponentType();
}

GenericArrayType 接口只有一个方法getGenericComponentType(),其可以用来获取数组方括号 [] 前面的部分,例如T[],在其上调用getGenericComponentType 就可以获得T.
值得注意的是多维数组得到的是最后一个[] 前面的部分,例如T[][], 得到的是T[].

翠花,上code:

//**********************GenericArrayType*********************
 Field tArray = TypeTest.class.getField("tArray");
 System.out.println("数组参数类型1:" + tArray.getGenericType());
 Field ltArray = TypeTest.class.getField("ltArray");
 System.out.println("数组参数类型2:" + ltArray.getGenericType());//数组参数类型2:java.util.List<T>[]
 if (tArray.getGenericType() instanceof GenericArrayType) {
     GenericArrayType arrayType = (GenericArrayType) tArray.getGenericType();
     System.out.println("数组参数类型3:" + arrayType.getGenericComponentType());//数组参数类型3:T
 }

输出:

数组参数类型1:T[]
数组参数类型2:java.util.List<T>[]
数组参数类型3:T

WildcardType:

通配符类型,即带有?的泛型参数, 例如 List<?>中的List<? extends Number>里的? extends NumberList<? super Integer>? super Integer

此接口源码如下:

public interface WildcardType extends Type {
   // 获取上界
	Type[] getUpperBounds();
	//获取下界
	Type[] getLowerBounds();
}

翠花,上code

 //***************************WildcardType*********************************
 Field mapWithWildcard = TypeTest.class.getField("mapWithWildcard");
 Type wild = mapWithWildcard.getGenericType();//先获取属性的泛型类型 Map<? super String, ? extends Number>
 if (wild instanceof ParameterizedType) {
     ParameterizedType pType = (ParameterizedType) wild;
     Type[] actualTypes = pType.getActualTypeArguments();//获取<>里面的参数变量 ? super String, ? extends Number
     System.out.println("WildcardType1:" + Arrays.asList(actualTypes));
     WildcardType first = (WildcardType) actualTypes[0];//? super java.lang.String
     WildcardType second = (WildcardType) actualTypes[1];//? extends java.lang.Number
     System.out.println("WildcardType2: lower:" + Arrays.asList(first.getLowerBounds()) + "  upper:" + Arrays.asList(first.getUpperBounds()));//WildcardType2: lower:[class java.lang.String]  upper:[class java.lang.Object]
     System.out.println("WildcardType3: lower:" + Arrays.asList(second.getLowerBounds()) + "  upper:" + Arrays.asList(second.getUpperBounds()));//WildcardType3: lower:[]  upper:[class java.lang.Number]
 }

输出:

WildcardType1:[? super java.lang.String, ? extends java.lang.Number]
WildcardType2: lower:[class java.lang.String]  upper:[class java.lang.Object]
WildcardType3: lower:[]  upper:[class java.lang.Number]

Class:

其是Type的一个实现类,是反射的基础,每一个类在虚拟机中都对应一个Calss 对象,我们可以用在运行时从这个Class对象中获取到类型所有信息。

 //**********************************Class*********************************
 Field tClass = TypeTest.class.getField("testClass");
 System.out.println("Class1:" + tClass.getGenericType());//获取泛型类型,由于我们这个属性声明时候没有使用泛型,所以会获得原始类型
 Field tClass2 = TypeTest.class.getField("testClass2");
 System.out.println("Class2:" + tClass2.getGenericType());//获取泛型类型

输出:

Class1:class typeInfo.TypeTest
Class2:typeInfo.TypeTest<T, java.lang.Integer>

可以看到 属性 public TypeTest testClass; 通过getGenericType()获取到的类型就是其原始类型TypeTest。而属性 public TypeTest<T, Integer> testClass2;获取到的则是ParameterizedType类型TypeTest<T, java.lang.Integer>

总结

过去,现在以及将来在任何领域前20%的人会得到这个领域80%的利益,余下的只能混口饭吃。将来顶级程序员与菜鸟程序员的境遇将会有千壤之别,所以越是原理性的知识越是要掌握,这是你拉开其他程序员等级之差的基础。希望有上进心的Java开发者,熟练掌握泛型这个Java中举足轻重的部分。

最后,求关注,求点赞!有任何疑问可以评论留言,我会尽力回复的。

  • 17
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ShuSheng007

亲爱的猿猿,难道你又要白嫖?

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值