更多详情内容请访问:JavaSE 系列文章导读

1、泛型概述

1.1 泛型介绍

Java 泛型(generics)是 JDK5 中引入的一个新特性,泛型提供了编译时类型安全监测机制,该机制允许我们在编译时检测到非法的类型数据结构;

泛型的好处:

  1. 类型安全

    使用泛型定义的变量的类型限制,编译器可以在一个高得多的程度上验证类型假设。没有泛型,这些假设就只存在于程序员的头脑中

  2. 消除强制类型转换

    消除源代码中的许多强制类型转换。这使得代码更加可读,并且减少了出错机会

  3. 潜在的性能收益

    在非泛型编程中,将筒单类型作为 Object 传递时会引起 Boxing(装箱)和 Unboxing(拆箱)操作,这两个过程都是具有很大开销的。引入泛型后,就不必进行 Boxing 和 Unboxing 操作了,所以运行效率相对较高,特别在对集合操作非常频繁的系统中,这个特点带来的性能提升更加明显

1.2 工作原理

泛型的正常工作是依赖编译器在编译源码的时候,先进行类型检查,然后进行类型擦除并且在类型参数出现的地方插入强制转换的相关指令实现的

1.3 基础接口

关于泛型的解析上面,我们需要先了解一些类和接口,这些比较关键,这些都位于 java.lang.reflect 包中,类图如下:

image-20220620205315930

这是一个顶层接口,Java 中的任何类型都可以用这个来表示,这个接口是 Java 编程语言中所有类型的公共超接口。这些类型包括原始类型、泛型类型、泛型变量类型、通配符类型、泛型数组类型、数组类型等各种类型。

image-20220620205832365

这个接口表示参数化类型,例如 List<String>Map<Integer, String>UserMapper<UserModel> 这种带有泛型的类型

  1. Type[] getActualTypeArguments():获取泛型类型中的类型列表,就是 <> 中包含的参数列表
  2. Type getRawType():返回参数化类型中的原始类型,就是 <> 符号前的部分
  3. Type[] getOwnerType():返回当前类型所属的类型,例如存在 A<T> 类,其中定义了内部类 InnerA<I>,则 InnerA<I> 所属的类型为 A<I>,如果是顶层类型则返回 null

所有声明泛型变量的公共接口,这个接口中定义了一个方法,这个方法用于获取声明的泛型变量类型清单:

image-20220620210211461

泛型变量可以在类和方法中进行声明,从上面类图中也可以看出来,Java 中任何类可以使用 Class 对象表示,方法可以用 Method 类表示,而 Class 类和 Method 类实现了 GenericDeclaration 接口,所以可以调用他们的 getTypeParameters 方法获取其声明的泛型参数列表

表示的是通配符泛型,通配符使用问号表示,例如:? extends Number 和 ? super Integer

  1. Type[] getUpperBounds():返回泛型变量的上边界列表
  2. Type[] getLowerBounds():返回泛型变量的下边界列表

这个接口表示的是泛型变量,例如:List<T> 中的 T 就是类型变量;而 class C1<T1,T2,T3>{} 表示一个类,这个类中定义了 3 个泛型变量类型,分别是 T1、T2 和 T2,泛型变量在 Java 中使用 TypeVariable 接口来表示,可以通过这个接口提供的方法获取泛型变量类型的详细信息

  1. Type[] getBounds():获取泛型变量类型的上边界,如果未明确什么上边界默认为 Object

    例如:class Test<K extend Person> 中K的上边界只有一个,是 Person;而 class Test<T extend List & Iterable> 中 T 的上边界有2个,是 List 和 Iterable

  2. D getGenericDeclaration():获取声明该泛型变量的原始类型

    例如:class Test<K extend Person> 中的 K 为泛型变量,这个泛型变量时 Test 类定义的时候声明的,说明如果调用 getGenericDeclaration 方法返回的就是 Test 对应的 Class 对象

  3. String getName():获取在源码中定义时的名字

    如:class Test<K extend Person>就是K;class Test1<T>中就是T

表示的是数组类型,且数组中的元素是 ParameterizedType 或者 TypeVariable,例如:List<String>[]或者T[]

Type getGenericComponentType():这个方法返回数组的组成元素

2、泛型变量

2.1 类中定义泛型变量

语法:class 类名<泛型变量1,泛型变量2,泛型变量3 extends 上边界1,泛型变量4 extends 上边界类型1 & 上边界类型2 & 上边界类型3>

  • 每个类中可以定义多个泛型变量,多个泛型变量之间用逗号隔开
  • 泛型变量可以通过 extends 关键字指定上边界,上边界可以对泛型变量起到了限定的作用,上边界可以指定 0 到多个,多个之间需要用 & 符号隔开,如果不指定上边界,默认上边界为 Object 类型

interface Demo1I1 {}

interface Demo1I2 {}

public class Demo1<T1, T2 extends Integer, T3 extends Demo1I1 & Demo1I2> {

    public static void main(String[] args) {
        TypeVariable<Class<Demo1>>[] typeParameters = Demo1.class.getTypeParameters(); // 会返回当前类上定义的泛型变量类型列表
        for (TypeVariable<Class<Demo1>> typeParameter : typeParameters) {
            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("--------------------");
        }
    }
}

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

2.2 方法中定义泛型变量

语法:方法修饰符 <泛型变量1,泛型变量2,泛型变量3 extends 上边界1,泛型变量4 extends 上边界类型1 & 上边界类型2 & 上边界类型3> 返回类型 方法名称(参数1类型 参数1名称,参数2类型 参数2名称)

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

interface Demo2I1 { }

interface Demo2I2 { }

public class Demo2 {

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

    public static void main(String[] args) {
        // 获取Demo2中声明的所有方法
        Method[] methods = Demo2.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);
                System.out.println("这个变量上边界清单:");
                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("--------------------");
        }

    }
}

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

3、泛型类型

泛型类定义语法:具体类型<类型1,类型2,类型3>

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

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

3.1 方法中泛型参数和泛型返回值

public class Demo4<T> { // Demo1<T> 声明了一个泛型类型的变量 T

    public class C1 {
        /**
         * m1 方法参数和返回值都是泛型类型,泛型的实际类型是泛型变量类型 T,T 是在 Demo4 中声明的
         * 创建了方法 m1,m1 的参数和返回值都是泛型类型的 List<T>,泛型类型在 java 中用 ParameterizedType 接口表示;
         * 而 List<T> 泛型类型中还有一个类型 T,T 是泛型变量类型的,在 java 中使用 TypeVariable 接口表示
         */
        public List<T> m1(List<T> list) {
            //对list做一些操作
            return list;
        }
    }

    public static void main(String[] args) throws NoSuchMethodException {
        // 获取 m1 方法
        Method m1 = Demo4.C1.class.getMethod("m1", List.class);
        // 调用 Method 中的 getGenericParameterTypes 方法可以获取参数类型列表,包含了详细的泛型信息
        Type arg1Type = m1.getGenericParameterTypes()[0];
        // m1 方法有1个参数是泛型类型的,泛型类型 java 中用 ParameterizedType 接口表示
        System.out.println("----------m1方法参数类型信息------------");
        if (arg1Type instanceof ParameterizedType) {
            ParameterizedType parameterizedType = (ParameterizedType) arg1Type;
            System.out.println("原始类型:" + parameterizedType.getRawType());
            System.out.println("所属的类型:" + parameterizedType.getOwnerType());
            Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
            // 泛型中第一个参数的类型是 T,T 是泛型变量,泛型变量对应 java 中的 TypeVariable 接口
            Type oneType = actualTypeArguments[0];
            System.out.println("@5:" + oneType.getClass());
            if (oneType instanceof TypeVariable) {
                System.out.println("这个参数是个泛型变量类型!");
                TypeVariable<Class<Demo4>> 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方法可以获取方法的返回值,如果返回值是泛型类型的,会获取泛型类型对应的具体类型,此处返回的是List<String>类型的,java中使用ParameterizedType接口表示
        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<Demo4>> 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方法参数类型信息------------
原始类型:interface java.util.List
所属的类型:null
@5:class sun.reflect.generics.reflectiveObjects.TypeVariableImpl
这个参数是个泛型变量类型!
变量名称:T
这个变量在哪声明的:class learn03_方法中泛型参数和返回值.Demo4
这个变量上边界数量:1
这个变量上边界清单:
java.lang.Object
----------m1方法返回值类型信息------------
原始类型:interface java.util.List
所属的类型:null
@8:class sun.reflect.generics.reflectiveObjects.TypeVariableImpl
返回值是个泛型变量类型!
变量名称:T
这个变量在哪声明的:class learn03_方法中泛型参数和返回值.Demo4
这个变量上边界数量:1
这个变量上边界清单:
java.lang.Object
--------------------

3.2 泛型类的定义

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

//泛型类
class Demo<T1, T2> { } // 这两个变量的具体类型,可以在创建对象的时候指定任意具体的类型

public class Demo6 extends Demo<String, Integer> { // 此时 T1 的具体类型就是 String 类型了,T2 对应的具体类型就是 Integer 类型

    public static void main(String[] args) {
        Demo6 demo6 = new Demo6();
        Class<? extends Demo6> demo6Class = demo6.getClass();

        // 获取Demo6的父类的详细类型信息,包含泛型信息
        Type genericSuperclass = demo6Class.getGenericSuperclass();

        // 泛型类型用 ParameterizedType 接口表示,输出看一下是不是这个接口类型的
        System.out.println(genericSuperclass.getClass());
        if (genericSuperclass instanceof ParameterizedType) { 
            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 learn04_泛型类定义.Demo
java.lang.String
java.lang.Integer
null

3.3 通配符类型

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

注意:此处 ? 是类型实参,而不是类型形参,也就是说他是和 Number、String、Integer 一样都是一种实际的类型,可以将其看成所有类的父类,是一种真实的类型。

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

  1. 通配符上边界:上边界指定了这个通配符能够表示的最大的范围的类型

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

  2. 通配符下边界:下边界定义了通配符能够表示的最小的类型

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

注意:为了提高效率,应该将标签接口(即没有方法的接口)放在限定列表的末尾

public class Demo8 {
    public static class C1 { }

    public static class C2 extends C1 { }

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

    public static void main(String[] args) throws NoSuchMethodException {
        Method m1 = Demo8.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) {
                ParameterizedType parameterizedType = (ParameterizedType) genericParameterType;
                // 下面获取 Map 后面两个尖括号中的泛型参数列表,对应 ? super C2, ? extends C1 这部分的内容,这部分在 java 中对应 WildcardType 接口类型
                Type[] actualTypeArguments = parameterizedType.getActualTypeArguments(); //@6
                for (Type actualTypeArgument : actualTypeArguments) {
                    if (actualTypeArgument instanceof WildcardType) {
                        WildcardType wildcardType = (WildcardType) actualTypeArgument;
                        // 获取通配符的名称,输出是?
                        System.out.println("通配符类型名称:" + wildcardType.getTypeName());//@7
                        //获取通配符的上边界
                        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("------------");
                    }
                }
            }
        }

        //获取返回值通配符详细信息
        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 learn05_通配符.Demo8$C2
通配符上边界类型:java.lang.Object
通配符下边界类型:learn05_通配符.Demo8$C2
------------
通配符类型名称:? extends learn05_通配符.Demo8$C1
通配符上边界类型:learn05_通配符.Demo8$C1
------------
获取m1方法返回值泛型类型详细信息
通配符类型名称:?
通配符上边界类型:java.lang.Object
------------

4、泛型数组

在java中是”不能创建一个确切的泛型类型的数组”

List<String>[] ls = new ArrayList<String>[10]; // error
List<?>[] ls = new ArrayList<?>[10];    // pass
List<String>[] ls = new ArrayList[10];  // pass

数组中的元素为泛型,那么这个数组就是泛型类型的数组,泛型数组在 Java 中使用 GenericArrayType 接口来表示,可以通过这个接口提供的方法获取泛型数组更详细的信息。比如:List<String> list [];

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

public class Demo9 {

    List<String> list[]; // 声明了一个泛型类型的数组

    public static void main(String[] args) throws NoSuchFieldException {
        Field list = Demo9.class.getDeclaredField("list");
        // 获取字段的泛型类型
        Type genericType = list.getGenericType();
        // 看看字段的具体泛型类型
        System.out.println(genericType.getClass());
        if (genericType instanceof GenericArrayType) {
            GenericArrayType genericArrayType = (GenericArrayType) genericType;

            // 获取数组的具体类型,具体的类型就是 List<String>,这个是个泛型类型,对应 java 中的 ParameterizedType 接口
            Type genericComponentType = genericArrayType.getGenericComponentType();
            System.out.println(genericComponentType.getClass());
            if (genericComponentType instanceof ParameterizedType) {
                ParameterizedType parameterizedType = (ParameterizedType) genericComponentType;
                System.out.println(parameterizedType.getRawType()); // interface java.util.List

                // 调用 getActualTypeArguments() 获取 List<String> 中尖括号中的参数列表
                Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
                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

5、泛型擦除

在编译期间,所有的泛型信息都会被擦除,List<Integer> 和 List<String> 类型,在编译后都会变成 List 类型(原始类型)这也是 Java 的泛型被称为“伪泛型”的原因

5.1 原始类型

原始类型就是泛型类型擦除了泛型信息后,在字节码中真正的类型。无论何时定义一个泛型类型,相应的原始类型都会被自动提供。原始类型的名字就是删去类型参数后的泛型类型的类名。擦除类型变量,并替换为限定类型(T为无限定的类型变量,用 Object 替换)

//泛型类型
class Pair<T> {  
    private T value;  
    public T getValue() {  
        return value;  
    }  
    public void setValue(T  value) {  
        this.value = value;  
    }  
}
//原始类型
class Pair {  
    private Object value;  
    public Object getValue() {  
        return value;  
    }  
    public void setValue(Object  value) {  
        this.value = value;  
    }  
}  

因为在 Pair<T> 中,T 是一个无限定的类型变量,所以用 Object 替换。如果是Pair<T extends Number>,擦除后,类型变量用 Number 类型替换

5.2 突破泛型约束

public class ReflectInGeneric {
    public static void main(String[] args) throws IllegalArgumentException, SecurityException, IllegalAccessException, InvocationTargetException, NoSuchMethodException {  
        ArrayList<Integer> array=new ArrayList<Integer>();  
        // 这样调用add方法只能存储整形,因为泛型类型的实例为Integer 
        array.add(1); 
        // 通过泛型可以突破泛型类型约束
        array.getClass().getMethod("add", Object.class).invoke(array, "asd");  
        for (int i=0;i<array.size();i++) {  
            System.out.println(array.get(i));  
        }  
    }  
}

在程序中定义了一个 ArrayList<Integer> 泛型类型,如果直接调用 add 方法,那么只能存储整形的数据,不过当利用反射调用add方法的时候,却可以存储字符串

这说明 ArrayList<Integer> 泛型信息在编译之后被擦除了,只保留了原始类型,类型变量(T)被替换为 Object,在运行时,我们可以往其中插入任意类型的对象。再次应证:Java中的泛型基本上都是在编译器这个层次来实现的“伪泛型”

但是,并不推荐以这种方式操作泛型类型,因为这违背了泛型的初衷(减少强制类型转换以及确保类型安全)。当我们从集合中获取元素时,默认会将对象强制转换成泛型参数指定的类型(这里是 Integer),如果放入了非法的对象这个强制转换过程就会出现异常

5.3 泛型方法的类型推断

在调用泛型方法的时候,可以指定泛型类型,也可以不指定。

  • 在不指定泛型类型的情况下,泛型类型为该方法中的几种参数类型的共同父类的最小级,直到 Object
  • 在指定泛型类型的时候,该方法中的所有参数类型必须是该泛型类型或者其子类
public class Test { 
    public static void main(String[] args) {  
        /**不指定泛型的时候*/  
        int i=Test.add(1, 2); // 这两个参数都是Integer,所以T替换为Integer类型  
        Number f=Test.add(1, 1.2);// 这两个参数一个是Integer,另一个是Float,所以取同一父类的最小级,为Number  
        Object o=Test.add(1, "asd");// 这两个参数一个是Integer,另一个是String,所以取同一父类的最小级,为Object

        /**指定泛型的时候*/  
        int a=Test.<Integer>add(1, 2);// 指定了Integer,所以只能为Integer类型或者其子类  
        int b=Test.<Integer>add(1, 2.2);// 编译错误,指定了Integer,不能为Float  
        Number c=Test.<Number>add(1, 2.2); // 指定为Number,所以可以为Integer和Float  
    }  

    // 这是一个简单的泛型方法  
    public static <T> T add(T x,T y){  
        return y;  
    } 
}

6、泛型相关问题

6.1 泛型类型引用传递问题

在 Java 中,像下面形式的引用传递是不允许的:

ArrayList<String> arrayList1=new ArrayList<Object>();// 编译错误  
ArrayList<Object> arrayList1=new ArrayList<String>();// 编译错误 
  1. 先看第一种情况,将其扩展成下面的形式:

    ArrayList<Object> arrayList1=new ArrayList<Object>();  
    arrayList1.add(new Object());  
    arrayList1.add(new Object());  
    ArrayList<String> arrayList2=arrayList1;// 编译错误  

    在第 4 行代码处,就会有编译错误。那么,我们先假设它编译没错。那么当我们使用 arrayList2 引用用 get() 方法取值的时候,返回的都是 String 类型的对象,可是它里面实际上已经被我们存放了 Object 类型的对象,这样,就会有 ClassCastException了。所以为了避免这种极易出现的错误,Java不允许进行这样的引用传递(这也是泛型出现的原因,就是为了解决类型转换的问题,不能违背它的初衷)

  2. 再看第二种情况,将其扩展成下面的形式:

    ArrayList<String> arrayList1=new ArrayList<String>();  
    arrayList1.add(new String());  
    arrayList1.add(new String());  
    ArrayList<Object> arrayList2=arrayList1; // 编译错误  

    这样的情况比第一种情况好的多,最起码,在用 arrayList2 取值的时候不会出现 ClassCastException,因为是从 String 转换为 Object。可是,泛型出现的原因就是为了解决类型转换的问题,我们使用了泛型,到头来,还是要自己强转,违背了泛型设计的初衷。所以 Java 不允许这么干。

6.2 泛型在静态方法和静态类中的问题

泛型类中的静态方法和静态变量不可以使用泛型类所声明的泛型类型参数

public class Test2<T> {     
    public static T one;   // 编译错误    
    public static T show(T one){ // 编译错误    
        return null;    
    }    
}  

因为泛型类中的泛型参数的实例化是在定义泛型类型对象(例如 ArrayList)的时候指定的,而静态变量和静态方法不需要使用对象来调用。对象都没有创建,如何确定这个泛型参数是何种类型,所以当然是错误的

但是要注意区分下面的一种情况:

public class Test2<T> {    
    public static <T> T show(T one){//这是正确的    
        return null;    
    }    
}  

因为这是一个泛型方法,在泛型方法中使用的 T 是自己在方法中定义的 T,而不是泛型类中的 T。即,如果静态方法要使用泛型的话,必须将静态方法也定义成泛型方法

7、通配符

7.1 通配符上界

public class Test {
    public static void printIntValue(List<? extends Number> list) {
        for (Number number : list) {
            System.out.print(number.intValue()+" "); 
        }
        System.out.println();
    }
    public static void main(String[] args) {
        List<Integer> integerList=new ArrayList<Integer>();
        integerList.add(2);
        integerList.add(2);
        printIntValue(integerList);
        List<Float> floatList=new ArrayList<Float>();
        floatList.add((float) 3.3);
        floatList.add((float) 0.3);
        printIntValue(floatList);
    }
}

public class Test {
    public static void fillNumberList(List<? extends Number> list) {
        list.add(new Integer(0));// 编译错误
        list.add(new Float(1.0));// 编译错误
    }
    public static void main(String[] args) {
        List<? extends Number> list=new ArrayList();
        list.add(new Integer(1));// 编译错误
        list.add(new Float(1.0));// 编译错误
    }
}

不能往 List<? extends T> 中添加任意对象,除了null,但可以对 List<? extends T> 进行迭代可以呢,因为子类必定有父类相同的接口

7.2 通配符下界

public class Test {
    public static void fillNumberList(List<? super Number> list) {
        list.add(new Integer(0));
        list.add(new Float(1.0));
    }
    public static void main(String[] args) {
        List<? super Number> list=new ArrayList(); 
        list.add(new Integer(1));
        list.add(new Float(1.1));
    }
}

List<? super Number> 可以代表 List<T>,其中 T 为 Number 父类,(虽然 Number 没有父类)。如果说,T 为 Number 的父类,想 List<T> 中加入 Number 的子类肯定是可以的

对 List<? superT> 进行迭代是不允许的。因为不知道用哪种接口去迭代 List,只有用 Object 类的接口才能保证集合中的元素都拥有该接口,显然这个意义不大。

7.3 无界通配符

知道了通配符的上界和下界,其实也等同于知道了无界通配符,不加任何修饰即可,单独一个“?”。如 List<?>,“?”可以代表任意类型,“任意”也就是未知类型。
List<Object> 与 List<?> 并不等同,List<Object> 是 List<?> 的子类。还有不能往 List<?> list 里添加任意对象,除了 null。List<?> 是一个未知类型的 List,而 List<Object> 其实是任意类型的 List

public class MyTest {

    public static void printList(List<?> list) { // 如果是 List<Object> list 则12、13行编译报错
        for (Object elem : list)
            System.out.println(elem + "");
        System.out.println();
    }

    public static void main(String[] args) {
        List<Integer> li = Arrays.asList(1, 2, 3);
        List<String>  ls = Arrays.asList("one", "two", "three");
        printList(li);
        printList(ls);
    }
}

END

本文作者:
文章标题:泛型
本文地址:https://www.pendulumye.com/java-foundation/184.html
版权说明:若无注明,本文皆PendulumYe原创,转载请保留文章出处。
最后修改:2022 年 08 月 29 日
千山万水总是情,给个一毛行不行💋