深入理解Java泛型(二)——示例和讲解

这是泛型第二篇文章,前面一篇文章全是概念知识。这篇文章就上一篇概念性知识举出示例进行讲解。
(补充一个知识点:instanceof详解,后面示例用了比较多instanceof关键字。)

一、泛型变量

泛型变量可以在类中和方法中定义。

泛型变量类型是使用TypeVariable接口来表示,所以可以通过TypeVariable接口获取泛型变量的所有信息

1、类中定义泛型变量

语法:

class 类名<泛型变量1, 泛型变量2, 泛型变量3 extends 上边界1, 泛型变量4 extends 上边界1 & 上边界2 & 上边界3>
  • 泛型变量需要在类名后面的括号中定义
  • 每个类中可以定义多个泛型变量,多个泛型变量直接用逗号隔开
  • 泛型变量可以通过extends关键字指定上边界,上边界可以对泛型变量起到限定作用,上边界可以指定0到多个,多个之间需要用&符号隔开,如果不指定上边界,默认上边界为Object类型

案例代码

package com.ydj.type;

import java.lang.reflect.*;

interface Demo1I1 { //@1
}

interface Demo1I2{  //@2
}

/**
 * 类中泛型变量案例
 * @param <T1>
 * @param <T2>
 * @param <T3>
 */
public class TypeDemo1<T1, T2 extends Integer, T3 extends Demo1I1 & Demo1I2> {  //@3
    public static void main(String[] args) {
        TypeVariable<Class<TypeDemo1>>[] typeVariable = TypeDemo1.class.getTypeParameters();	//@4
        for (TypeVariable<Class<TypeDemo1>> typeParameter : typeVariable) {
            System.out.println("变量名称:" + typeParameter.getName());
            System.out.println("这个变量在哪声明的:" + typeParameter.getGenericDeclaration());
            Type[] bounds = typeParameter.getBounds();
            System.out.println("这个变量上边界数量:" + bounds.length);
            System.out.println("这个变量上边界清单:");
            for (Type bound : bounds) {
                System.out.println(bound.getTypeName());
            }
            System.out.println("------------------");
        }
    }
}

代码解读:

  • @1:创建接口Demo1I1,后面会用到
  • @2:创建接口Demo1I2,后面会用到
  • @3:创建了一个类TypeDemo1,注意这个类是泛型类型的,泛型中定义了3个泛型变量,分别是:T1、T2、T3,这三个变量是有区别的。

T1没有限制上边界,默认上边界就是Object类型了。

注意T2的写法:

T2 extends Integer

这个通过extends关键字限定了T2的上边界为Integer。

再看看T3的写法,比较特别:

T3 extends Demo1I1 & Demo1I2

T3的上边界有多个,多个上边界之间需要用 & 连接起来,T3有2个上边界,分别是两个接口Demo1I1和Demo1I2。

  • @4:这行代码用来调用了Class对象的getTypeParameters方法,这个方法返回当前类上定义的泛型变量类型列表,当前类上定义了3个泛型变量类型,泛型变量类型在Java中使用TypeVariable接口表示。

后面的for循环就是输出泛型变量的信息了,我们来看一下运行效果:

变量名称:T1
这个变量在哪声明的:class com.ydj.type.TypeDemo1
这个变量上边界数量:1
这个变量上边界清单:
java.lang.Object
------------------
变量名称:T2
这个变量在哪声明的:class com.ydj.type.TypeDemo1
这个变量上边界数量:1
这个变量上边界清单:
java.lang.Integer
------------------
变量名称:T3
这个变量在哪声明的:class com.ydj.type.TypeDemo1
这个变量上边界数量:2
这个变量上边界清单:
com.ydj.type.Demo1I1
com.ydj.type.Demo1I2
------------------

输出中可以看到3个泛型变量都是在当前类TypeDemo1中定义的,每个泛型变量的名称以及泛型变量的上边界信息都详细输出来了。

2、方法中定义泛型变量

语法

方法修饰符 <泛型变量1, 泛型变量2, 泛型变量3 extends 上边界1,  泛型变量4 extends 上边界1 & 上边界2 & 上边界3> 方法名称(参数1类型 参数1名称, 参数2类型 参数2名称)
  • 泛型变量需要在方法名称前面的括号中定义
  • 方法中可以定义多个泛型变量,多个泛型变量之间用逗号隔开
  • 泛型变量可以通过extends关键字指定上边界,上边界对泛型变量起到了限定作用,上边界可以指定0到多个,多个之间需要用 & 符号隔开,如果不指定上边界,默认上边界为Object类型

案例代码

package com.ydj.type;


import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;

interface Demo2I1{  //@1
}

interface Demo2I2{  //@2
}

public class TypeDemo2 {

    public <T1, T2 extends Integer, T3 extends Demo2I1 & Demo2I2>T3 m1 (T1 t1, T2 t2, T3 t3, String s){ //@3
        return t3;
    }

    public static void main(String[] args) {
        //获取TypeDemo2中声明的所有方法
        Method[] methods = TypeDemo2.class.getDeclaredMethods();
        Method m1 = null;

        //找到m1方法
        for (Method method : methods) {
            if(method.getName().equals("m1")){
                m1 = method;
                break;
            }
        }

        //获取方法的泛型参数列表
        System.out.println("----------m1方法参数类型列表信息:----------");
        Type[] genericParameterTypes = m1.getGenericParameterTypes();
        for (Type genericParameterType :genericParameterTypes) {
            //3个参数都是泛型变量类型的,对应Java中的TypeVariable
            if(genericParameterType instanceof TypeVariable){
                TypeVariable pt = (TypeVariable) genericParameterType;
                System.out.println("变量类型名称:"+pt.getTypeName());
                System.out.println("变量名称:"+pt.getName());
                System.out.println("这个变量在哪声明的:"+pt.getGenericDeclaration());
                Type[] bounds = pt.getBounds();
                System.out.println("这个变量上边界数量:"+bounds.length);
                for (Type bound: bounds) {
                    System.out.println(bound.getTypeName());
                }
            }else if(genericParameterType instanceof Class){
                Class pt = (Class) genericParameterType;
                System.out.println("参数类型名称:" + pt.getTypeName());
                System.out.println("参数名称:" + pt.getName());
            }
            System.out.println("------------------");
        }

        //获取方法的返回值,也是一个泛型变量
        System.out.println("----------m1方法返回值类型信息:----------");
        Type genericReturnType = m1.getGenericReturnType();
        if(genericReturnType instanceof TypeVariable){
            TypeVariable pt = (TypeVariable) genericReturnType;
            System.out.println("变量名称:" + pt.getName());
            System.out.println("这个变量在哪声明的:" + pt.getGenericDeclaration());
            Type[] bounds = pt.getBounds();
            System.out.println("这个变量上边界数量:" + bounds.length);
            System.out.println("这个变量上边界清单" );
            for (Type bound: bounds) {
                System.out.println(bound.getTypeName());
            }
            System.out.println("------------------");
        }

        //获取方法中声明的泛型参数列表
        System.out.println("----------m1方法中声明的泛型变量类型列表:----------");
        TypeVariable<Method>[] typeParameters = m1.getTypeParameters();
        for (TypeVariable<Method> pt: typeParameters) {
            System.out.println("变量类型名称:" + pt.getTypeName());
            System.out.println("变量名称:" + pt.getName());
            System.out.println("这个变量在哪声明的:" + pt.getGenericDeclaration());
            Type[] bounds = pt.getBounds();
            System.out.println("这个变量上边界数量:" + bounds.length);
            System.out.println("这个变量上边界清单:");
            for (Type bound : bounds) {
                System.out.println(bound.getTypeName());
            }
            System.out.println("--------------------");
        }

    }

}

  • @1和@2声明接口,下面会使用
  • @3:这行比较特别,创建了一个方法,如下:
public <T1, T2 extends Integer, T3 extends Demo2I1 & Demo2I2> T3 m1(T1 t1, T2 t2, T3 t3, String s)

m1方法前面的<>括号中定义了3个泛型类型变量,方法有4个参数,前3个参数的类型为泛型变量类型,第4个参数为String类型。

泛型变量类型在Java中使用TypeVariable表示,前3个参数都是泛型变量类型,所以最后他们的信息都可以使用TypeVariable接口获取,最后一个参数是String类型,这个是非泛型类型,使用Class类型表示。

上面代码中先获取m1方法对应的Method对象,然后通过Method中的方法获取了方法参数的列表、方法的返回值详细的泛型信息、方法中声明的3个泛型变量的详细信息。

结果如下:

----------m1方法参数类型列表信息:----------
变量类型名称:T1
变量名称:T1
这个变量在哪声明的:public com.ydj.type.Demo2I1 com.ydj.type.TypeDemo2.m1(java.lang.Object,java.lang.Integer,com.ydj.type.Demo2I1,java.lang.String)
这个变量上边界数量:1
java.lang.Object
------------------
变量类型名称:T2
变量名称:T2
这个变量在哪声明的:public com.ydj.type.Demo2I1 com.ydj.type.TypeDemo2.m1(java.lang.Object,java.lang.Integer,com.ydj.type.Demo2I1,java.lang.String)
这个变量上边界数量:1
java.lang.Integer
------------------
变量类型名称:T3
变量名称:T3
这个变量在哪声明的:public com.ydj.type.Demo2I1 com.ydj.type.TypeDemo2.m1(java.lang.Object,java.lang.Integer,com.ydj.type.Demo2I1,java.lang.String)
这个变量上边界数量:2
com.ydj.type.Demo2I1
com.ydj.type.Demo2I2
------------------
参数类型名称:java.lang.String
参数名称:java.lang.String
------------------
----------m1方法返回值类型信息:----------
变量名称:T3
这个变量在哪声明的:public com.ydj.type.Demo2I1 com.ydj.type.TypeDemo2.m1(java.lang.Object,java.lang.Integer,com.ydj.type.Demo2I1,java.lang.String)
这个变量上边界数量:2
这个变量上边界清单
com.ydj.type.Demo2I1
com.ydj.type.Demo2I2
------------------
----------m1方法中声明的泛型变量类型列表:----------
变量类型名称:T1
变量名称:T1
这个变量在哪声明的:public com.ydj.type.Demo2I1 com.ydj.type.TypeDemo2.m1(java.lang.Object,java.lang.Integer,com.ydj.type.Demo2I1,java.lang.String)
这个变量上边界数量:1
这个变量上边界清单:
java.lang.Object
--------------------
变量类型名称:T2
变量名称:T2
这个变量在哪声明的:public com.ydj.type.Demo2I1 com.ydj.type.TypeDemo2.m1(java.lang.Object,java.lang.Integer,com.ydj.type.Demo2I1,java.lang.String)
这个变量上边界数量:1
这个变量上边界清单:
java.lang.Integer
--------------------
变量类型名称:T3
变量名称:T3
这个变量在哪声明的:public com.ydj.type.Demo2I1 com.ydj.type.TypeDemo2.m1(java.lang.Object,java.lang.Integer,com.ydj.type.Demo2I1,java.lang.String)
这个变量上边界数量:2
这个变量上边界清单:
com.ydj.type.Demo2I1
com.ydj.type.Demo2I2
--------------------

二、泛型类型

泛型类型定义的语法

具体类型<类型1, 类型2, 类型3>
  • 泛型类型可以作为方法的参数、方法的返回值、泛型类(这3种会一一举例)
  • <>中的泛型的实际参数列表,可以有多个,可以是任意类型的。比如:String类型、自定义类型、泛型变量类型、泛型通配符类型(?表示通配符)
  • 泛型类型的信息在Java中使用ParameterizedType接口来表示,可以通过这个接口作为入口获取泛型的具体详细信息。

比如:List< String >、Map< Integer, String >、UserMapper< UserModel >,这些都是泛型类型,这些泛型的信息都可以通过ParameterizedType来表示,然后通过这个接口中的方法获取这些泛型的具体信息。

下面来详解3种泛型类型。

1、方法中泛型参数和泛型返回值

方法的参数为泛型类型或者返回值为泛型类型,我们来获取这些泛型类型的信息。

案例代码

package com.ydj.type;

import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.util.List;

public class TypeDemo3<T> { //@0

    public class C1{    //@1
        /**
         * m1方法参数和返回值都是泛型类型,泛型的实际类型是泛型变量类型T,T是在TypeDemo3中声明的
         * @param list
         * @return
         */
        public List<T> m1(List<T> list){    //@2
            //对list做一些操作
            return list;
        }
    }

    public static void main(String[] args) throws NoSuchMethodException {
        //获取m1方法
        Method m1 = TypeDemo3.class.getMethod("m1", List.class);
        //调用Method中getGenericParameterTypes方法可以获取参数类型列表,包含了详细的泛型信息
        Type arg1Tyep = m1.getGenericParameterTypes()[0];
        //m1方法有1个参数是泛型类型的,泛型类型Java中用ParameterizedType接口表示
        System.out.println("----------m1方法参数类型信息------------");
        if (arg1Tyep instanceof ParameterizedType){ //@3
            ParameterizedType parameterizedType = (ParameterizedType) arg1Tyep;
            System.out.println("原始类型:" + parameterizedType.getRawType());
            System.out.println("所属的类型:" + parameterizedType.getOwnerType());
            Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
            //泛型中第一个参数的类型是T,T是泛型变量,泛型变量对应Java中的TypeVariable接口
            Type oneType = actualTypeArguments[0];  //@4
            System.out.println("@5:" + oneType.getClass()); //@5
            if(oneType instanceof TypeVariable){
                System.out.println("这个参数是个泛型变量类型!");
                TypeVariable<Class<TypeDemo3>> oneActualType = (TypeVariable) oneType;
                System.out.println("变量名称:" + oneActualType.getName());
                System.out.println("这个变量在哪声明的:" + oneActualType.getGenericDeclaration());
                Type[] bounds = oneActualType.getBounds();
                System.out.println("这个变量上边界数量:" + bounds.length);
                System.out.println("这个变量上边界清单:");
                for (Type bound:bounds) {
                    System.out.println(bound.getTypeName());
                }
            }
        }


        System.out.println("----------m1方法返回值类型信息------------");
        //m1方法返回值是泛型类型的,泛型类型Java中用ParameterizedType接口表示
        //Method类中的getGenericReturnType方法可以获取方法的返回值,如果返回值是泛型类型的,会获取泛型类型对应的具体类型
        Type returnType = m1.getGenericReturnType();
        if(returnType instanceof ParameterizedType){    //@6
            ParameterizedType parameterizedType = (ParameterizedType) returnType;
            System.out.println("原始类型:" + parameterizedType.getRawType());
            System.out.println("所属的类型:" + parameterizedType.getOwnerType());
            Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
            //泛型中第一个参数的类型是T,T是泛型变量,泛型变量对应Java中的TypeVariable接口
            Type oneType = actualTypeArguments[0];  //@7
            System.out.println("@8:" + oneType.getClass()); //@8
            if (oneType instanceof TypeVariable) {
                System.out.println("返回值是个泛型变量类型!");
                TypeVariable<Class<TypeDemo3>> oneActualType = (TypeVariable) oneType;
                System.out.println("变量名称:" + oneActualType.getName());
                System.out.println("这个变量在哪声明的:" + oneActualType.getGenericDeclaration());
                Type[] bounds = oneActualType.getBounds();
                System.out.println("这个变量上边界数量:" + bounds.length);
                System.out.println("这个变量上边界清单:");
                for (Type bound : bounds) {
                    System.out.println(bound.getTypeName());
                }
                System.out.println("--------------------");
            }
        }
    }
}

代码解读

  • @0:TypeDemo3< T >声明了一个泛型类型的变量T;T是个泛型类型的变量,泛型类型的变量在Java中使用TypeVariable来表示
  • @1:创建了一个类C1,注意这个类是在TypeDemo3的内部声明的,说明C1是一个内部类
  • @2:创建了一个方法m1,m1的参数和返回值都是泛型类型的List< T >,泛型类型在Java中用ParameterizedType接口表示;而List< T >泛型类型中还有一个类型T,T是泛型变量类型的,在Java中使用TypeVariable接口表示

上面代码中输出了m1方法参数的泛型的详细信息,结果如下:

----------m1方法参数类型信息------------
原始类型:interface java.util.List
所属的类型:null
@5:class sun.reflect.generics.reflectiveObjects.TypeVariableImpl
这个参数是个泛型变量类型!
变量名称:T
这个变量在哪声明的:class com.ydj.type.TypeDemo3
这个变量上边界数量:1
这个变量上边界清单:
java.lang.Object
----------m1方法返回值类型信息------------
原始类型:interface java.util.List
所属的类型:null
@8:class sun.reflect.generics.reflectiveObjects.TypeVariableImpl
返回值是个泛型变量类型!
变量名称:T
这个变量在哪声明的:class com.ydj.type.TypeDemo3
这个变量上边界数量:1
这个变量上边界清单:
java.lang.Object
--------------------

2、泛型类

泛型类的定义

类修饰符 类名<类型1, 类型2, 类型n>{
}

上面是定义了一个泛型类,<>中包含的是一些可以变类型的列表,实际上我们创建这个类的对象的时候,会明确指定<>中包含的具体类型。

比如我们熟悉的HashMap就是一个泛型类,来看看这个类的定义:

public class HashMap<K, V>

K和V是泛型变量类型的,具体是什么类型,可以在创建HashMap的时候去随意指定。

现在我们想获取泛型对象<>中包含的具体的类型,怎么获取呢?

比如下面代码:

package com.ydj.type;

public class TypeDemo4<T1, T2> {    //@1

    public void m1(TypeDemo4<T1, T2> demo){ //@2
        System.out.println(demo.getClass());
    }

    public static void main(String[] args) {
        TypeDemo4<String, Integer> demo4 = new TypeDemo4<>();   //@3
        demo4.m1(demo4);
    }
}
  • @1:TypeDemo4类中定义了两个泛型变量类型T1和T2
  • @2:m1方法参数类型为TypeDemo4,在这个方法内部,如果我们想获取这个参数具体的详细类型信息,上面的代码是获取不到的,只能获取到demo4参数所属的类型是TypeDemo4,但是无法获取到TypeDemo4中的T1和T2这两个泛型变量类型对应的具有类型。

运行一下上面代码,结果如下:

class com.ydj.type.TypeDemo4

Class对象中有个方法比较牛逼:

public Type getGenericSuperclass()

这个方法相当牛逼,可以获取到父类中泛型详细信息。

来看一个案例就明白了:

package com.ydj.type;

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;

class Demo<T1, T2>{ //@0
}

public class TypeDemo5 extends Demo<String, Integer> {  //@1

    public static void main(String[] args) {
        TypeDemo5 demo5 = new TypeDemo5();
        //demo5Class对应的是TypeDemo5的Class对象
        Class<? extends TypeDemo5> demo5Class = demo5.getClass();   //@2

        //获取TypeDemo5的父类的详细信息,包含泛型信息
        Type genericSuperclass = demo5Class.getGenericSuperclass(); //@3
        //泛型类型用ParameterizedType接口表示,输出看一下是不是这个接口类型
        System.out.println(genericSuperclass.getClass());   //@4
        if(genericSuperclass instanceof ParameterizedType){	//@5
            ParameterizedType pt = (ParameterizedType) genericSuperclass;
            System.out.println("父类原始类型:" + pt.getRawType());

            Type[] actualTypeArguments = pt.getActualTypeArguments();
            for (Type actualTypeArgument:actualTypeArguments) {
                System.out.println(actualTypeArgument.getTypeName());
            }
            System.out.println("父类所属类型:" + pt.getOwnerType());
        }
    }
}

运行输出:

class sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl
父类原始类型:class com.ydj.type.Demo
java.lang.String
java.lang.Integer
父类所属类型:null
  • @0:声明了一个泛型类,泛型类中定义了两个泛型变量的类型T1和T2,这两个变量的具体类型,可以在创建对象的时候指定任意具体的类型
  • @1:这个比较特殊了,创建了TypeDemo5,这个类继承了Demo类,并且注意Demo后面的部分< String, Integer >,这个指定了具体的类型了,此时T1的具体类型就是String类型了,T2对应的具体类型就是Integer类型了
  • @2:获取TypeDemo5对应的Class对象
  • @3:这行代码比较关键,这个调用了Class类中的getGenericSupperclass方法,这个方法可以获取当前父类的具体类型信息,如果父类是泛型,则会返回泛型详细信息,泛型类型在Java中用ParameterizedType接口表示,所以@3代码返回的类型一定是ParameterizedType接口类型了
  • @4:输出了genericSuperclass变量的类型,注意上面第2行输出:ParameterizedTypeImpl,这个是ParameterizedType接口的一个实现类。
  • @5:这个地方加了一个判断,判断是不是ParameterizedType类型的,然后if内部输出了整个泛型类型的具体的信息,调用了ParameterizedType接口中的3个方法去获取了具体的参数类型的信息,输出中的5、6行可以看到输出了具体的类型String和Integer。

根据上面代码的原理,我们可以将下面的代码进行改造:

package com.ydj.type;

public class TypeDemo4<T1, T2> {    //@1

    public void m1(TypeDemo4<T1, T2> demo){ //@2
        System.out.println(demo.getClass());
    }

    public static void main(String[] args) {
        TypeDemo4<String, Integer> demo4 = new TypeDemo4<>();   //@3
        demo4.m1(demo4);
    }
}

如果我们想获得TypeDemo4的具体信息,需要给TypeDemo4创建一个子类才可以,此处我们可以使用Java中的匿名内部类来友好地解决这个问题,将上面代码变换一下,变成下面这样:

package com.ydj.type;

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;

public class TypeDemo4<T1, T2> {    //@1

    public void m1(TypeDemo4<T1, T2> demo){ //@2
        //demo4Class对应的是TypeDemo4的Class对象
        Class<? extends TypeDemo4> demo4Class = demo.getClass();
        //获取demo父类的详细类型信息,包含泛型信息
        Type genericSuperclass = demo4Class.getGenericSuperclass();
        //泛型类型用ParameterizedType接口表示,输出看下这个接口类型
        System.out.println(genericSuperclass.getClass());

        if(genericSuperclass instanceof ParameterizedType){
            ParameterizedType pt = (ParameterizedType)genericSuperclass;
            System.out.println("原始类型:" + pt.getRawType());
            Type[] actualTypyArguments = pt.getActualTypeArguments();
            for (Type actualTypeArgument: actualTypyArguments) {
                System.out.println(actualTypeArgument.getTypeName());
            }
            System.out.println("所属类型:" + pt.getOwnerType());
        }
    }

    public static void main(String[] args) {
        TypeDemo4<String, Integer> demo4 = new TypeDemo4<String, Integer>(){
        };   //@3
        demo4.m1(demo4);
    }
}

关键代码在@3,这个地方利用了一个匿名内部类,相当于创建了TypeDemo4的一个子类,并且指定了TypeDemo4中两个泛型变量类型的具体类型。

运行代码输出如下:

class sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl
原始类型:class com.ydj.type.TypeDemo4
java.lang.String
java.lang.Integer
所属类型:null

这次我们获取到了泛型类中具体的类型了。

这种玩法在fastjson中有用到,再来看个案例:

package com.ydj.type;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import lombok.*;

import java.io.Serializable;


public class TypeDemo6 {

    @Getter
    @Setter
    @ToString
    @Builder
    @NoArgsConstructor
    @AllArgsConstructor
    public static class Result<T> implements Serializable{  //@1
        private String code;
        private String subCode;
        private String msg;
        private T data;
    }


    @Getter
    @Setter
    @ToString
    @Builder
    @NoArgsConstructor
    @AllArgsConstructor
    public static class UserModel{  //@2
        private Integer id;
        private String name;
    }

    /**
     * 返回一个用户信息
     *
     * @return
     */
    public static Result<UserModel> getUser(){  //@3
        UserModel userModel = UserModel.builder().id(1).name("xxxx").build();
        Result<UserModel> result = Result.<UserModel>builder().code("1").subCode(null).msg("操作成功").data(userModel).build();
        return result;
    }


    /**
     * 返回用户Json格式信息
     *
     * @return
     */
    public static String getUserString(){   //@4
        return JSON.toJSONString(getUser());
    }

    public static void main(String[] args) {
        String userString = getUserString();
        //会输出:{"code":"1","data":{"id":1,"name":"xxxx"},"msg":"操作成功"}
        System.out.println(userString); //@5

        //下面我们需要将userString反序列化为Result<UserModel>对象
        Result<UserModel> userModelResult = JSON.parseObject(userString, new TypeReference<Result<UserModel>>(){
        }); //@6

        //我们来看看Result中的data是不是UserModel类型的
        System.out.println(userModelResult.getData().getClass());   //@7

    }
}

先看看运行结果:

{"code":"1","data":{"id":1,"name":"xxxx"},"msg":"操作成功"}
class com.ydj.type.TypeDemo6$UserModel
  • @1:创建了一个Result类型的,这个类型可以作为任何接口通用的返回值类型,这个大家可以借鉴。接口有几个通用的字段:code(状态码),subCode(子状态码),data(具体的任何类型的数据,data的具体类型可以在Result的时候指定),msg(接口返回的提示信息,如错误提示或操作成功等信息)
  • @2:创建了一个用户类
  • @3:这个方法模拟返回一个用户信息,用户信息封存在Result中
  • @4:将用户信息转换为json字符串返回
  • @5:输出了用户信息字符串,也就是上面输出中的第一行内容
  • @6:这个是上面代码的关键,调用了fastjson中的方法,将字符串反序列化为Result< UserModel >,fastjson是如何获取泛型类Result中的T的具体类型的呢?T具体的类型对应的是UserModel,关键代码就是下面这行代码:
new TypeReference<Result<UserModel>>{
}

这个相当于创建了一个TypeReference类的一个子类,注意TypeReference后面尖括号中的东西,是< UserModel >,通过这个指定了泛型变量类型的具体类型,我们去看一下TypeReference类源码,贴出来一些关键代码:

public class TypeReference<T>{
	protected TypeReference(){
		Type superClass = this.getClass().getGenericSuperclass();	//@1
		
        Type type = ((ParameterizedType)superClass).getActualTypeArguments()[0];	//@2
        
        Type cachedType = (Type)classTypeCache.get(type);
        if (cachedType == null) {
            classTypeCache.putIfAbsent(type, type);
            cachedType = (Type)classTypeCache.get(type);
        }

        this.type = cachedType;
	}
}

注意上面的@1和@2是不是很熟悉了,fastjson中获取泛型的具体类型也是让我们采用匿名类去实现的,最后内部也是调用getGenericSuperclass去获取具体的泛型类型中具体的类型的。

3、通配符类型

通配符在Java中使用 ?表示,例如:? extends Number 和 ? super Integer。

Java中通配符对应的类型是WildcardType接口,可以通过这个接口来获取通配符具体的各种信息。

通配符上边界

通配符具体的类型,可以任意指定,但是我们可以限定通配符的上边界,上边界指定了这个通配符能够表示的最大的范围的类型

比如:? extends Integer,那么 ?对应的具体类型只能是Integer本身或者其子类型。


通配符下边界

也可以给通配符指定下边界,下边界定义了通配符能够表示的最小的类型

比如:? super C1,那么 ?对应的具体类型只能是C1类型或者C1的父类型。

package com.ydj.type;

import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.WildcardType;
import java.util.List;
import java.util.Map;

public class TypeDemo7 {

    public static class C1{}    //@1

    public static class C2 extends C1{}     //@2

    public static List<?> m1(Map<? super C2, ? extends C1> map){    //@3
        return null;
    }

    public static void main(String[] args) throws NoSuchMethodException {

        //获取m1方法
        Method m1 = TypeDemo7.class.getMethod("m1", Map.class);
        //获取m1方法参数泛型详细参数信息
        System.out.println("-------------获取m1方法参数泛型详细参数信息-------------");
        Type[] genericParameterTypes = m1.getGenericParameterTypes();
        for (Type genericParameterType:genericParameterTypes) {
            //m1的参数为Map<? super C2, ? extends C1>,这个是泛型类型的,所以是ParameterizedType接口类型
            if(genericParameterType instanceof ParameterizedType){  //@4
                ParameterizedType pt = (ParameterizedType)genericParameterType;
                //下面获取Map后面两个尖括号中的泛型参数列表,对应? super C2和? extends C1这部分内容
                //这部分内容在Java中对应WildcardType接口类型
                Type[] actualTypeArguments = pt.getActualTypeArguments();
                for (Type actualTypeArgument:actualTypeArguments) {
                    if(actualTypeArgument instanceof WildcardType){
                        WildcardType wt = (WildcardType)actualTypeArgument;
                        //获取通配符的名称,输出是?
                        System.out.println("通配符类型名称:" + wt.getTypeName());

                        //获取通配符的上边界
                        Type[] upperBounds = wt.getUpperBounds();
                        for (Type upperBound:upperBounds) {
                            System.out.println("通配符上边界类型:" + upperBound.getTypeName());
                        }
                        //获取通配符的上边界
                        Type[] lowerBounds = wt.getUpperBounds();
                        for (Type lowerBound:lowerBounds) {
                            System.out.println("通配符下边界类型:" + lowerBound.getTypeName());
                        }
                        System.out.println("----------------");
                    }
                }
            }


        }

        //获取返回值通配符详细信息
        System.out.println("-------------获取m1方法返回值泛型类型详细信息-------------");
        Type genericReturnType = m1.getGenericReturnType();
        // m1的返回值是List<?>,这个是个泛型类型,对应ParameterizedType接口,泛型中的具体类型是个通配符类型,通配符对应WildcardType接口类型
        if (genericReturnType instanceof ParameterizedType) { //@4
            ParameterizedType parameterizedType = (ParameterizedType) genericReturnType; //@5
            //下面获取List面两个尖括号中的泛型参数列表,对应?这部分的内容,这个是个通配符类型,这部分在java中对应WildcardType接口
            Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
            for (Type actualTypeArgument : actualTypeArguments) {
                if (actualTypeArgument instanceof WildcardType) {
                    WildcardType wildcardType = (WildcardType) actualTypeArgument;
                    //获取通配符的名称,输出是?
                    System.out.println("通配符类型名称:" + wildcardType.getTypeName());
                    //获取通配符的上边界
                    Type[] upperBounds = wildcardType.getUpperBounds();
                    for (Type upperBound : upperBounds) {
                        System.out.println("通配符上边界类型:" + upperBound.getTypeName());
                    }
                    //获取通配符的下边界
                    Type[] lowerBounds = wildcardType.getLowerBounds();
                    for (Type lowerBound : lowerBounds) {
                        System.out.println("通配符下边界类型:" + lowerBound.getTypeName());
                    }
                    System.out.println("------------");
                }
            }
        }
    }
}

结果为:

-------------获取m1方法参数泛型详细参数信息-------------
通配符类型名称:? super com.ydj.type.TypeDemo7$C2
通配符上边界类型:java.lang.Object
通配符下边界类型:java.lang.Object
----------------
通配符类型名称:? extends com.ydj.type.TypeDemo7$C1
通配符上边界类型:com.ydj.type.TypeDemo7$C1
通配符下边界类型:com.ydj.type.TypeDemo7$C1
----------------
-------------获取m1方法返回值泛型类型详细信息-------------
通配符类型名称:?
通配符上边界类型:java.lang.Object
------------

具体的就不解释了,和上面的案例类似。只需要注意一点 ?通配符的信息使用WildcardType接口表示,可以通过这个接口获取通配符的详细信息。

三、泛型数组

什么是泛型数组?

数组中的元素为泛型,那么这个数组就是泛型类型的数组,泛型数组在Java中使用GenericArrayType接口表示,可以通过这个接口提供的方法获取泛型数组更详细的信息。

如:List< String > list [];List< String > list [][];

泛型数组类型的可以作为方法的参数、方法的返回值、泛型类的具体类型、字段的类型等待…

下面就以泛型字段来举例,一起来获取泛型字段的详细信息吧!

package com.ydj.type;

import java.lang.reflect.Field;
import java.lang.reflect.GenericArrayType;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.List;

public class TypeDemo8 {

    List<String> list[];    //@1

    public static void main(String[] args) throws NoSuchFieldException {
        //通过反射包里面的Field类和Class类获取方法所有信息
        Field list = TypeDemo8.class.getDeclaredField("list");
        //获取字段的泛型类型
        Type genericType = list.getGenericType();   //@2
        //看看字段的具体泛型类型
        System.out.println(genericType.getClass());  //@3
        if (genericType instanceof GenericArrayType) {
            GenericArrayType genericArrayType = (GenericArrayType) genericType;
            //获取数组的具体类型,具体的类型就是List<String>,这个是个泛型类型,对应java中的ParameterizedType接口
            Type genericComponentType = genericArrayType.getGenericComponentType();//@4
            System.out.println(genericComponentType.getClass());
            if (genericComponentType instanceof ParameterizedType) {
                ParameterizedType parameterizedType = (ParameterizedType) genericComponentType;
                System.out.println("原始类型:" + parameterizedType.getRawType());
                //调用getActualTypeArguments()获取List<String>中尖括号中的参数列表
                Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();//@5
                for (Type actualTypeArgument : actualTypeArguments) {
                    System.out.println(actualTypeArgument.getTypeName());
                }
                System.out.println("所属类型:" + parameterizedType.getOwnerType());
            }
        }
    }
}

运行输出:

class sun.reflect.generics.reflectiveObjects.GenericArrayTypeImpl
class sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl
原始类型:interface java.util.List
java.lang.String
所属类型:null

代码解读

  • @1:声明了一个泛型类型的数组
  • @2:获取list字段对应的泛型数组类型,泛型数组在Java中使用GenericArrayType表示,所以@3输出的是GenericArrayType接口类型的
  • @4:调用了GenericArrayType接口中的getGenericComponentType方法,会返回数组的具体的泛型类型,这个地方对应的就是List< String >,这个是个泛型类型,泛型类型在Java中使用ParameterizedType接口表示
  • @5:调用了ParameterizedType接口中的getActualTypeArguments方法,这个方法可以获取泛型类型中具体的类型列表,就是List< String >中的String参数列表

四、综合案例

package com.ydj.type;

import java.util.List;
import java.util.Map;

public class TypeDemo9<K, V> {

    Map<String, ? extends List<? extends Map<K,V>>> [][] map;
    
}

上面这个看似几条语句,但是挺复杂的,我们一步步拆解解析,步骤如下:

1、TypeDemo9:对应Java中的Class对象
2、<K, V>:定义了2个泛型变量,泛型变量对应Java中的TypeVariable接口
3、Map<String, ? extends List<? extends Map<K, V>>> [][] map:定义了一个二维泛型数组,泛型数组在Java中用GenericArrayType接口表示
4、mp中的每个元素是这个Map<String, ? extends List<? extends Map<K, V>>> []类型的,是一个一维泛型数组,泛型数组在Java中用GenericArrayType接口表示
5、再继续拆解,Map<String, ? extends List<? extends Map<K, V>>> []中每个元素都是Map<String, ? extends List<? extends Map<K, V>>>泛型类型的,泛型类型在Java中用ParameterizedType接口表示
6、拆解Map<String, ? extends List<? extends Map<K, V>>>这个泛型类型中<>尖括号里面的参数列表,可以调用ParameterizedType接口的Type[] getActualTypeArguments()方法获取Map<String, ? extends List<? extends Map<K, V>>>泛型类型中<>尖括号里面的2个参数,分别是String和? extends List<? extends Map<K, V>>
7、String是Java中定义的类型,对应Java中的Class对象
8、? extends List<? extends Map<K, V>>是通配符类型的,对应Java中的WildcardType接口,通配符指定了上边界,上边界是List<? extends Map<K, V>>
9、List<? extends Map<K, V>>又是一个泛型类型,泛型类型在Java中用ParameterizedType接口表示;List<? extends Map<K, V>>中<>尖括号里面又定义了这个泛型类型的具体的类型 ? extends Map<K, V>
10、? extends Map<K, V>又是一个通配符类型,对应Java中WildcardType接口,这个通配符指定了上边界为Map<K, V>
11、Map<K, V>又是一个泛型类型,泛型类型对应Java中的ParameterizedType接口,调用这个接口的Type[] getActualTypeArguments()方法获取泛型中的参数列表K和V
12、K和V是TypeDemo9中定义的泛型变量类型,泛型变量类型对应Java中的TypeVariable接口

按照上面的思路,我们来完善一下代码:

package com.ydj.type;

import java.lang.reflect.*;
import java.util.List;
import java.util.Map;

public class TypeDemo9<K, V> {

    Map<String, ? extends List<? extends Map<K,V>>> [][] map;

    public static void parseType(Type type, int level) {
        String whileString = whileString(level);
        //1、泛型数组类型
        if (type instanceof GenericArrayType) {
            System.out.println(whileString + "泛型数组类型:" + type);
            parseType(((GenericArrayType) type).getGenericComponentType(), ++level);
        } else if (type instanceof ParameterizedType) {//泛型类型
            System.out.println(whileString + "泛型类型:" + type);
            ParameterizedType parameterizedType = (ParameterizedType) type;
            System.out.println(whileString + "原始类型:" + parameterizedType.getRawType());
            //获取泛型参数的详细信息
            Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
            System.out.println(whileString + actualTypeArguments.length + "个泛型参数,如下:");
            int count = 0;
            for (Type actualTypeArgument : actualTypeArguments) {
                if (count++ == 0) {
                    level++;
                }
                parseType(actualTypeArgument, level);
            }
        } else if (type instanceof WildcardType) {//通配符类型
            System.out.println(whileString + "通配符类型:" + type);
            WildcardType wildcardType = (WildcardType) type;
            System.out.println(whileString + "通配符类型名称:" + wildcardType.getTypeName());
            Type[] upperBounds = wildcardType.getUpperBounds();
            System.out.println(whileString + "上边界列表:");
            int count = 0;
            for (Type upperBound : upperBounds) {
                if (count++ == 0) {
                    level++;
                }
                parseType(upperBound, level);
            }
            System.out.println(whileString + "下边界列表:");
            Type[] lowerBounds = wildcardType.getLowerBounds();
            for (Type lowerBound : lowerBounds) {
                if (count++ == 0) {
                    level++;
                }
                parseType(lowerBound, level);
            }
        } else if (type instanceof TypeVariable) {//泛型变量类型
            System.out.println(whileString + "泛型变量类型:" + type);
            TypeVariable typeVariable = (TypeVariable) type;
            Type[] bounds = typeVariable.getBounds();
            System.out.println(whileString + "泛型变量上边界列表:");
            int count = 0;
            for (Type bound : bounds) {
                if (count++ == 0) {
                    level++;
                }
                parseType(bound, level);
            }
        } else if (type instanceof Class) {//普通类型
            System.out.println(whileString + "普通类型:" + ((Class) type).getName());
        }
    }

    /**
     * 根据“----”符号,来对比泛型级别,分别阅读和区分
     *
     * @param count 级别
     * @return
     */
    public static String whileString(int count){
        StringBuilder sb = new StringBuilder();
        for(int i = 0; i < count; i++){
            sb.append("----");
        }
        return sb.toString();
    }

    public static void main(String[] args) throws NoSuchFieldException {
        //获取TypeDemo9的字段信息
        Field field = TypeDemo9.class.getDeclaredField("map");
        Type genericType = field.getGenericType();
        parseType(genericType, 0);
    }

}

运行输出:

泛型数组类型:java.util.Map<java.lang.String, ? extends java.util.List<? extends java.util.Map<K, V>>>[][]
----泛型数组类型:java.util.Map<java.lang.String, ? extends java.util.List<? extends java.util.Map<K, V>>>[]
--------泛型类型:java.util.Map<java.lang.String, ? extends java.util.List<? extends java.util.Map<K, V>>>
--------原始类型:interface java.util.Map
--------2个泛型参数,如下:
------------普通类型:java.lang.String
------------通配符类型:? extends java.util.List<? extends java.util.Map<K, V>>
------------通配符类型名称:? extends java.util.List<? extends java.util.Map<K, V>>
------------上边界列表:
----------------泛型类型:java.util.List<? extends java.util.Map<K, V>>
----------------原始类型:interface java.util.List
----------------1个泛型参数,如下:
--------------------通配符类型:? extends java.util.Map<K, V>
--------------------通配符类型名称:? extends java.util.Map<K, V>
--------------------上边界列表:
------------------------泛型类型:java.util.Map<K, V>
------------------------原始类型:interface java.util.Map
------------------------2个泛型参数,如下:
----------------------------泛型变量类型:K
----------------------------泛型变量上边界列表:
--------------------------------普通类型:java.lang.Object
----------------------------泛型变量类型:V
----------------------------泛型变量上边界列表:
--------------------------------普通类型:java.lang.Object
--------------------下边界列表:
------------下边界列表:

上面将map这个属性详细的泛型信息都输出来了,重点在于上面的parseType方法。Java中的类型无非就是5种表示类型,这个方法内部使用递归来解析这些类型。

总结

泛型解析需要一步步拆解,会被拆解为5中类型中的一种,需要理解5种类型分别对应什么,这是关键。

你如果认真看到这里,再回过头看看第一篇开头出现的那幅图,可能会更加明白一点哦。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值