注意:本文中使用 ResolvableType 是 Spring framework 中提供的工具类,用于简化获取类的信息,该类位于 spring-core 项目下,想使用的小伙伴引入 spring-core 项目即可使用。

似乎,从学习 java 的范型开始,我所认识的范型便是会被擦除的,但是,这似乎并不能覆盖所有场景,我指的是并不是所有场景下面的范型都会被擦除,只是部分场景下的范型会被擦除。下面我们一起来看一段代码。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
class A extends ArrayList<String> {
}
public class Test {
    public static void main(String[] args) throws NoSuchMethodException, NoSuchFieldException {
        List<String> list = new ArrayList<>();
        ResolvableType generic1 = ResolvableType
                .forInstance(list)
                .getGeneric(0);
        System.out.println(generic1.getRawClass()); // null

        ResolvableType generic4 = ResolvableType
                .forClass(A.class)
                .getSuperType()
                .getGeneric(0);
        System.out.println(generic4); // java.lang.String
    }
}

从上面的例子可以看到,我们在代码中直接声明的 List<String> 确实获取不到范型,但是,我们声明的 A 这个类,却获取到了他父类的范型,这便能说明某些场景下的范型是被存起来的。至于原因目前我也不太说得上来,只是通过本文记录这样一个想法。

关于这个原因我有以下想法,这只是一个猜测不一定正确,这里面的关键在于用 class 关键字定义的类会在编译的时候将泛型信息保存到 class 文件中,所以能找到,而直接在 new 的时候写的泛型,java 很可能只是执行泛型检查,然后便将泛型擦除掉,所以在使用的时候找不到。

对于哪些场景的范型会被记录下来,看我下面这个例子就好了,如果读者在别的场景想要获取范型,测试一下就知道能否获取到了。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
class A extends ArrayList<String> {
    private List<String> test;

    public void test(List<String> list) {

    }
}

public class Test {
    public static void main(String[] args) throws NoSuchMethodException, NoSuchFieldException {
        List<String> list = new ArrayList<>();
        ResolvableType generic1 = ResolvableType
                .forInstance(list)
                .getGeneric(0);
        System.out.println(generic1.getRawClass()); // null

        ResolvableType generic2 = ResolvableType
                .forMethodParameter(A.class.getDeclaredMethod("test", List.class), 0)
                .getGeneric(0);
        System.out.println(generic2); // java.lang.String

        ResolvableType generic3 = ResolvableType
                .forField(A.class.getDeclaredField("test"))
                .getGeneric(0);
        System.out.println(generic3); // java.lang.String

        ResolvableType generic4 = ResolvableType
                .forClass(A.class)
                .getSuperType()
                .getGeneric(0);
        System.out.println(generic4); // java.lang.String
    }
}