【Java核心】Java泛型(Generic Type)

一、写在前面。

       错误可分为两种:编译时错误与运行时错误。编译时错误在编译时可以发现并排除,而运行时错误具有很大的不确定性,在程序运行时才能发现,造成的后果可能是灾难性的。

       泛型的引入使得一部分错误可以提前到编译时期发现,极大地增强了代码的健壮性。但是我们知道 java 泛型在运行的时候是会进行泛型擦除的,那我们要怎样得到在编译时期泛型的信息呢?Java 为我们提供了 Type 接口,使用它,我们可以得到这些信息。

        类型擦除是指泛型在运行的时候会去除泛型的类型信息。java 中,泛型主要是在编译层次来实现的,在生成的字节码即 class 文件是不包括泛型的类型信息的。 即 List, List ,List 虽然在编译时候是不同的,但是在编译完成后,在class 文件中都只会把他们当作 List 来对待。

二、Type 接口

简单来说:Type是所有类型的父接口, 如原始类型(raw types 对应 Class)、 参数化类型(parameterized types 对应 ParameterizedType)、 数组类型(array types 对应 GenericArrayType)、 类型变量(type variables 对应 TypeVariable )和基本(原生)类型(primitive types 对应 Class),。

子接口有 ParameterizedType, TypeVariable, GenericArrayType, WildcardType, 实现类有Class。

【Java核心】Java泛型(Generic Type)_第1张图片

三、实例

   ParameterizedType (参数化类型)

/**
 *  参数化泛型
 */
public interface ParameterizedType extends Type {
    /**
     * 这个 Type 类型的参数的实际类型数组。 如 Map map 
       这个ParameterizedType 返回的是 String 类,Person 类的全限定类名的 Type Array。
     */
    Type[] getActualTypeArguments();

    /**
     * 返回的是当前这个 ParameterizedType 的类型。 如 Map map 
       这个ParameterizedType 返回的是 Map 类的全限定类名的 Type Array。
     */
    Type getRawType();

    /**
     * 返回表示此类型所属类型的对象。如Map.Entry entry; 
       得到的是interface java.util.Map$Entry。
      (可以简单理解为:外部类与内部类的关系,此操作为获取该对象(内部类)的外部类)
     */
    Type getOwnerType();
}
import java.util.List;
import java.util.Map;
import java.util.Set;

public class ParameterizedTypeBean {
    // 下面的 field 的 Type 属于 ParameterizedType
    Map map;
    Set set1;
    Class clz;
    Holder holder;
    List list;
    // Map map 这个 ParameterizedType 的 getOwnerType() 为 null,
    // 而 Map.Entry entry 的 getOwnerType() 为 Map 所属于的 Type。
    Map.Entry entry;
    // 下面的 field 的 Type 不属于ParameterizedType
    String str;
    Integer i;
    Set set;
    List aList;

    static class Holder {

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

public class Test {

    public static void main(String[] args) {
        testParameterizedType();
    }
    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];
                System.out.println(f.getName()
                        + " getGenericType() instanceof ParameterizedType "
                        + (f.getGenericType() instanceof ParameterizedType));
            }
            System.out.println("-----------------");
            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);
        System.out.println(f.getGenericType());
        boolean b=f.getGenericType() instanceof ParameterizedType;
        System.out.println(b);
        if(b){
            ParameterizedType pType = (ParameterizedType) f.getGenericType();
            System.out.println(pType.getRawType());
            System.out.println("&&&&&&&&&&&&&&&&&&&");
            for (Type type : pType.getActualTypeArguments()) {
                System.out.println(type);
            }
            System.out.println(pType.getOwnerType()); // null
        }
        System.out.println("***********************");
    }
}
map getGenericType() instanceof ParameterizedType true
set1 getGenericType() instanceof ParameterizedType true
clz getGenericType() instanceof ParameterizedType true
holder getGenericType() instanceof ParameterizedType true
list getGenericType() instanceof ParameterizedType true
entry getGenericType() instanceof ParameterizedType true
str getGenericType() instanceof ParameterizedType false
i getGenericType() instanceof ParameterizedType false
set getGenericType() instanceof ParameterizedType false
aList getGenericType() instanceof ParameterizedType false
-----------------
java.util.Map
true
interface java.util.Map
&&&&&&&&&&&&&&&&&&&
class java.lang.String
class com.nuc.zp.sourcecode.types.Person
null
***********************
java.util.Map$Entry
true
interface java.util.Map$Entry
&&&&&&&&&&&&&&&&&&&
class java.lang.String
class java.lang.String
interface java.util.Map
***********************

TypeVariable

/**
 * TypeVariable
 */
public interface TypeVariable extends Type, AnnotatedElement {
    /**
     * 得到上边界的 Type数组,如 K 的上边界数组是 InputStream 和 Serializable。 V 没有指定的话,上边界是 Object
    */
    Type[] getBounds();

    /**
     * 返回的是声明这个 Type 所在的类 的 Type
     */
    D getGenericDeclaration();

    /**
     * 返回的是这个 type variable 的名称
     */
    String getName();

    /**
     * AnnotatedType表示当前在此VM中运行的程序中可能注释使用的类型。 
       该用途可以是Java编程语言中的任何类型,包括数组类型,参数化类型,类型变量或通配符类型。
     * 通过AnnotatedType的getType()方法,获得其上边界的 Type,如 K 的上边界数组是 InputStream 和 Serializable。
       V 没有指定的话,上边界是 Object
     */
     AnnotatedType[] getAnnotatedBounds();
}
public class TypeVariableBean {
    // K 的上边界是 InputStream
    K key;
    // 没有指定的话 ,V 的 上边界 属于 Object
    V value;
    // 不属于 TypeTypeVariable
    V[] values;
    String str;
    List kList;
    public static void main(String[] args) throws NoSuchFieldException {
        Field fk = TypeVariableBean.class.getDeclaredField("key");
        TypeVariable keyType = (TypeVariable) fk.getGenericType();
        for (Type type : keyType.getBounds()) {
            System.out.println(type);
        }
        System.out.println("---------------------");
        for (AnnotatedType annotatedType : keyType.getAnnotatedBounds()) {
            System.out.println(annotatedType +"----"+annotatedType.getType());
        }
        System.out.println("***********************");
        System.out.println(keyType.getName());
        System.out.println(keyType.getGenericDeclaration());
    }
}
class java.io.InputStream
interface java.io.Closeable
---------------------
sun.reflect.annotation.AnnotatedTypeFactory$AnnotatedTypeBaseImpl@4c873330----class java.io.InputStream
sun.reflect.annotation.AnnotatedTypeFactory$AnnotatedTypeBaseImpl@119d7047----interface java.io.Closeable
***********************
K
class com.nuc.zp.sourcecode.types.TypeVariableBean

GenericArrayType

public class GenericArrayTypeBean {
    // 属于 GenericArrayType
    public List[] pTypeArray;
    // 属于 GenericArrayType
    T[] vTypeArray;
    // 不属于 GenericArrayType
    List list;
    // 不属于 GenericArrayType
    String[] strings;
    // 不属于 GenericArrayType
    Person[] ints;

    public static void main(String[] args) {
        testGenericArrayType();
    }

    public void test(List[] pTypeArray, T[] vTypeArray,
                     List list, String[] strings, Person[] ints) {
    }

    public static void testGenericArrayType() {
        for (Method method : GenericArrayTypeBean.class.getDeclaredMethods()) {
            System.out.println(method);
        }
        System.out.println("--------------------------------");
        Method method = GenericArrayTypeBean.class.getDeclaredMethods()[1];
        System.out.println(method);
        // public void test(List[] pTypeArray, T[]
        // vTypeArray,List list, String[] strings, Person[] ints)
        Type[] types = method.getGenericParameterTypes(); // 这是 Method 中的方法
        for (Type type : types) {
            System.out.println(type + "--------" + (type instanceof GenericArrayType));// 依次输出true,true,false,false,false
        }
    }
}
public static void com.nuc.zp.sourcecode.types.GenericArrayTypeBean.main(java.lang.String[])
public void com.nuc.zp.sourcecode.types.GenericArrayTypeBean.test(java.util.List[],java.lang.Object[],java.util.List,java.lang.String[],com.nuc.zp.sourcecode.types.Person[])
public static void com.nuc.zp.sourcecode.types.GenericArrayTypeBean.testGenericArrayType()
--------------------------------
public void com.nuc.zp.sourcecode.types.GenericArrayTypeBean.test(java.util.List[],java.lang.Object[],java.util.List,java.lang.String[],com.nuc.zp.sourcecode.types.Person[])
java.util.List[]--------true
T[]--------true
java.util.List--------false
class [Ljava.lang.String;--------false
class [Lcom.nuc.zp.sourcecode.types.Person;--------false

WildcardType

public interface WildcardType extends Type {
    /**
     * 上边界
     */
    Type[] getUpperBounds();

    /**
     * 下边界
     */
    Type[] getLowerBounds();
}
/**
 * 通配符类型
 */
public class WildcardTypeBean {
    private List a;  // a没有下界,
    //  没有指定的话,上边界默认是 Object ,下边界是  String
    private List b;

    private List c;

    private Class aClass;

    public static void main(String[] args) {
        testWildCardType();
    }

    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();
                System.out.println("下面开始打印" + nameString + "是否具有通配符");
                if (!(type instanceof ParameterizedType)) {
                    System.out.println("---------------------------");
                    continue;
                }
                ParameterizedType parameterizedType = (ParameterizedType) type;
                type = parameterizedType.getActualTypeArguments()[0];
                if (!(type instanceof WildcardType)) {
                    System.out.println("---------------------------");
                    continue;
                }
                WildcardType wildcardType = (WildcardType) type;
                Type[] lowerTypes = wildcardType.getLowerBounds();
                if (lowerTypes != null) {
                    System.out.println("下边界");
                    printTypeArr(lowerTypes);
                }
                Type[] upTypes = wildcardType.getUpperBounds();
                if (upTypes != null) {
                    System.out.println("上边界");
                    printTypeArr(upTypes);
                }
                System.out.println("---------------------------");

            }
            Field fieldA = WildcardTypeBean.class.getDeclaredField("a");
            Field fieldB = WildcardTypeBean.class.getDeclaredField("b");
            // 先拿到范型类型
            System.out.println(fieldA.getGenericType() instanceof ParameterizedType);
            System.out.println(fieldB.getGenericType() instanceof ParameterizedType);
            ParameterizedType pTypeA = (ParameterizedType) fieldA.getGenericType();
            ParameterizedType pTypeB = (ParameterizedType) fieldB.getGenericType();
            // 再从范型里拿到通配符类型
            System.out.println(pTypeA.getActualTypeArguments()[0] instanceof WildcardType);
            System.out.println(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();
        }

    }

    public static void printTypeArr(Type[] types){
        for (Type type: types){
            System.out.println(type);
        }
    }
}
下面开始打印a是否具有通配符
下边界
上边界
class java.lang.Number
---------------------------
下面开始打印b是否具有通配符
下边界
class java.lang.String
上边界
class java.lang.Object
---------------------------
下面开始打印c是否具有通配符
---------------------------
下面开始打印aClass是否具有通配符
下边界
上边界
class java.lang.Object
---------------------------
true
true
true
true
class java.lang.Number
class java.lang.String
? extends java.lang.Number

四、Spring对泛型的处理:ResolvableType

/**
 * 测试继承获取泛型
 */
public class Parent {
}
/**
 * 测试泛型
 */
public interface IParent {
}
@Slf4j
public class Children extends Parent implements IParent {

    public static void main(String[] args) {
        /**
         * 这里是获取父类中泛型,如果有多个也是一样的方式!获取到的泛型参数还可能是 通配符表达式,
         * 这里也是可以处理的,多个判断而已
         */
        Type genericSuperclassType = Children.class.getGenericSuperclass();
        if (genericSuperclassType instanceof ParameterizedType) {
            Type[] actualTypeArguments = ((ParameterizedType) genericSuperclassType)
                    .getActualTypeArguments();
            for (Type argumentType : actualTypeArguments) {
                log.info("父类ParameterizedType.getActualTypeArguments:" + argumentType);
            }
        }
        /**
         * 这里获取父接口中的泛型参数
         */
        Type[] genericInterfacesTypes = Children.class.getGenericInterfaces();
        for (Type interfaceType : genericInterfacesTypes) {
            if (interfaceType instanceof ParameterizedType) {
                Type[] actualTypeArguments = ((ParameterizedType) interfaceType)
                        .getActualTypeArguments();
                for (Type argumentType : actualTypeArguments) {
                    log.info("父接口ParameterizedType.getActualTypeArguments:" + argumentType);
                }
            }
        }
        /**
         言归正传,下面讲解ResolvableType。ResolvableType为所有的java类型提供了统一的数据结构以及API
         ,换句话说,一个ResolvableType对象就对应着一种java类型。
         我们可以通过ResolvableType对象获取类型携带的信息
         (举例如下):
         1.getSuperType():获取直接父类型
         2.getInterfaces():获取接口类型
         3.getGeneric(int...):获取类型携带的泛型类型
         4.resolve():Type对象到Class对象的转换

         另外,ResolvableType的构造方法全部为私有的,我们不能直接new,只能使用其提供的静态方法进行类型获取:
         1.forField(Field):获取指定字段的类型
         2.forMethodParameter(Method, int):获取指定方法的指定形参的类型
         3.forMethodReturnType(Method):获取指定方法的返回值的类型
         4.forClass(Class):直接封装指定的类型
         */
        ResolvableType superResolvableType = ResolvableType.forClass(Children.class).getSuperType();
        log.info("supper:"+superResolvableType.resolveGenerics()[0]);

        ResolvableType superInterfaceResolvableType = ResolvableType.
                forClass(Children.class) .getInterfaces()[0];
        log.info("supper:"+superInterfaceResolvableType.resolveGenerics()[0]);

    }
}
13:17:37.073 [main] INFO com.nuc.zp.sourcecode.types.Children - 父类ParameterizedType.getActualTypeArguments:class java.lang.String
13:17:37.084 [main] INFO com.nuc.zp.sourcecode.types.Children - 父接口ParameterizedType.getActualTypeArguments:class java.lang.Long
13:17:37.111 [main] INFO com.nuc.zp.sourcecode.types.Children - supper:class java.lang.String
13:17:37.111 [main] INFO com.nuc.zp.sourcecode.types.Children - supper:class java.lang.Long

参考文章:https://blog.csdn.net/u012881904/article/details/80813294

                  https://blog.csdn.net/shenchaohao12321/article/details/80282833

你可能感兴趣的:(Spring,Java)