类型系统

java类型系统.png

Class

  • 最常见的 Type 实现类
  • 代表原始类型(raw types)和基本类型(primitive types):
1
2
Class<?> clazz = String.class;
Class<?> arrayClass = int[].class;

ParameterizedType(参数化类型)

  • 表示带有泛型参数的类型
  • 主要方法:
    • getActualTypeArguments(): 获取泛型参数的实际类型
    • getRawType(): 获取原始类型
    • getOwnerType(): 获取所属类型
1
2
3
4
5
6
// Map<String, List<Integer>>
Map<String, List<Integer>> map = new HashMap<>();
Type type = map.getClass().getGenericSuperclass();
if (type instanceof ParameterizedType) {
Type[] actualTypeArguments = ((ParameterizedType) type).getActualTypeArguments();
}

TypeVariable(类型变量)

  • 表示泛型中的类型变量
  • 例如泛型定义中的 T、K、V 等
1
2
3
4
5
6
7
8
public class Container<T> {
private T value;

public void showType() {
TypeVariable<?>[] typeParameters = Container.class.getTypeParameters();
// typeParameters[0] 就是 T
}
}

GenericArrayType(泛型数组类型)

  • 表示元素类型是参数化类型或类型变量的数组
  • 例如:T[] 或 List[]
1
2
3
4
5
6
7
8
9
10
11
12
public class Example<T> {
T[] array;
List<String>[] lists;

public void check() {
Field arrayField = Example.class.getDeclaredField("array");
Type arrayType = arrayField.getGenericType();
if (arrayType instanceof GenericArrayType) {
Type componentType = ((GenericArrayType) arrayType).getGenericComponentType();
}
}
}

WildcardType(通配符类型)

  • 表示通配符泛型,比如 ?、? extends Number、? super Integer
1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class WildcardExample {
List<? extends Number> numbers;

public void examine() {
Field field = WildcardExample.class.getDeclaredField("numbers");
Type type = field.getGenericType();
if (type instanceof ParameterizedType) {
Type[] actualTypeArguments = ((ParameterizedType) type).getActualTypeArguments();
WildcardType wildcard = (WildcardType) actualTypeArguments[0];
Type[] upperBounds = wildcard.getUpperBounds(); // 获取上界
Type[] lowerBounds = wildcard.getLowerBounds(); // 获取下界
}
}
}

主要使用场景:

  1. 反射获取泛型信息
  2. 泛型类型系统的实现
  3. 框架开发中的类型判断和处理
  4. 注解处理器的开发
  5. 序列化/反序列化框架

这些类型系统的实现让 Java 能够在运行时获取和处理泛型信息,尽管 Java 的泛型是通过类型擦除实现的,但通过这些接口我们仍然可以在运行时获取到泛型的类型信息。

基本语法

  • java 的泛型没有 template 关键字。
  • 三种基本概念:
    • Type Parameter(类型形参):在泛型类、接口或方法声明时使用的标识符。
      • class Box<T> 中的 T。
    • Type Variable(类型变量):类型参数在代码中的引用。
      • 可以认为是类型参数的一个”实例”。在 class Box<T> { T value; } 中,字段类型T是一个类型变量。许多文档中”类型参数”和”类型变量”可互换使用。
    • Type Argument(类型实参):在使用泛型类型时,提供的具体类型。
      • 在调用或实例化时提供。Box 中的 Integer。
        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        // 类型参数T在类声明中引入
        public class List<T> {
        // T用作类型变量
        private T[] elements;

        // T继续用作类型变量
        public void add(T element) {...}

        // E是新的类型参数,用于此方法
        public <E> void addAll(Collection<E> items, Function<E, T> converter) {...}
        }

        // String是类型实参,实例化时提供
        List<String> names = new ArrayList<>();
  • 泛型方法的 type variable 在 modifier(public static)和return value之间。
  • List<T> 是 generic type。List<String>是 Parameterized type。

绑定类型(bounding type)

在Java泛型的上下文中,”bound”一词最准确的翻译是“边界”(boundary)而非”绑定”。Java官方文档一致使用”upper bound”(上边界)和”lower bound”(下边界)这样的术语,这些术语在数学和集合论中表示边界概念。

  • 明确要绑定 type variable 到某个类型-得到 type argument,才可以在接下来的方法里调用那个类型的方法,如需要 compareTo 方法,就必须<T extends Comparable<T>>extends本身可以后接多个绑定类型,用&分隔。

递归泛型边界模式(Recursive Generic Type Bounds)

这是被称作”F-bounded多态”(F-bounded polymorphism)或”递归泛型边界”的高级泛型设计模式。

1
2
public class RepresentationModel<T extends RepresentationModel<? extends T>> {
}
  • T必须是RepresentationModel的子类
  • 并且这个子类的泛型参数必须是T自身或T的某个子类

RepresentationModel - type parameter 是 -> T <- 是子类,且这个子类的 type argument 是 T 自己 - RepresentationModel

如果不使用这种类型:

1
2
3
4
5
6
7
8
9
10
11
12
// 不使用递归泛型
public class ResourceModel {
public ResourceModel addLink(String rel, String href) {
// 添加链接的逻辑
return this;
}
}

public class UserModel extends ResourceModel {
private String username;
// 其他用户特有属性和方法...
}

那么使用时会遭遇编译错误:

1
2
3
4
5
UserModel user = new UserModel();
// 返回类型是ResourceModel,而不是UserModel
ResourceModel result = user.addLink("self", "/users/123");
// 编译错误:ResourceModel类型没有getUsername()方法
String username = result.getUsername();

这个问题的核心是,有些 fluent API 本身返回自身,这种自身不能返回子类时,父子混合 build 会产生奇怪的问题。

解法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class RepresentationModel<T extends RepresentationModel<? extends T>> {
public T addLink(String rel, String href) {
// 添加链接的逻辑
return self();
}

@SuppressWarnings("unchecked")
private T self() {
return (T) this;
}
}

public class UserModel extends RepresentationModel<UserModel> {
private String username;

public String getUsername() {
return username;
}
}

使用时:

1
2
3
4
5
UserModel user = new UserModel();
// 现在返回类型仍然是UserModel
UserModel result = user.addLink("self", "/users/123");
// 完全类型安全,编译通过
String username = result.getUsername();

这种方案的做法是:

  1. 父类所有方法都使用 T 作为 type variable。在父转子的时候,使用转型,忽略警告。
  2. 流式调用的时候都转为自己。

核心优势:

  • 类型安全的方法链:子类调用父类方法时返回的仍然是子类类型
  • 代码复用:父类可以实现通用功能,子类无需重写这些方法
  • IDE支持:智能提示和自动完成可以正确显示返回对象的可用方法

这种模式广泛应用于各种Java库中:

  • Java的Enum<E extends Enum<E>>
    • public abstract class Enum<E extends Enum<E>> implements Comparable<E>, Serializable {}
      • 确保类型安全:> 这种递归泛型定义确保了枚举类型只能被其子类实例化
      • 提供类型正确的方法:使得像 compareTo 这样的方法能够正确接收子类类型的参数
      • 实现自引用泛型:允许枚举类引用自身类型
  • JPA的CriteriaBuilder
  • Stream API-易被忽略
  • 各种构建器模式实现

类型擦除(type erasure)

  • 类型擦除的结果的是将 type variable 替换成绑定类型(bounding types)的 ordinary class(或者说使用 raw type 的 class)。
  • 有了类型擦除,所有的泛型表达式都是可以被翻译的。带有泛型的代码被编译器翻译后通常分为两个部分:1. ordinary class,带有 bounding type,2. casting code,带有类型转换的代码。
  • cast code 是为了保证类型安全而存在的。
  • 假设B extends A<C>,我们调用calc(new C())的时候,父类型的相关方法总是会被擦除到 bounding type(假定 C 的擦除类型是 Object),所以子类的方法也带有这个 bounding type 的方法实现calc(Object object),但为了保持多态,编译器又生成了一个calc(C c)(相当于做了一次重载),真正要使用多态,就必须产生一个 synthesized bridge method,执行calc(Object object) {calc((C)object);}。这个东西是为了保持多态(preserve polymorphism)。泛型集成对比点 1。
  • 为了兼容性(compatibility)考虑,A a = new A<String>() 之类的赋值(从泛型到擦除类型的赋值总是会成立的)总是会成立,但编译器总是会报 warning。猫插入狗列表中问题,只有在真实地 set 和 get 操作时才会发生。在 Jackson 中经常遇见如下问题:实际反序列化的生成 class 是 LinkedHashMap,但Entity<A<B>> = JsonUtil.toObject(str)等还是会赋值成功(此处 实际得到的是A<LinkedHashMap>)。

Type Witness 类型见证/目击者

1
2
3
4
5
6
7
8
9
// 抽象模式
SomeClass.<TypeName>methodName(arguments)
// 当没有足够的上下文进行推断时:
var result = Collections.<String>emptyList(); // 没有var需要Type Witness
// 在复杂的泛型嵌套中:
someMethod(Collections.<Map<String, Integer>>emptyList());

// 在 java7 及以前,这行代码可能不能编译。processStringList 可能是个泛型,而实参赋值的时候无法推导列表类型
processStringList(Collections.emptyList());

其中第二种情况更常见,如果方法调用/=左边有足够的目标类型,编译器不会产生有歧义的推导(Inference)路径。

泛型不能做什么

所有泛型的 bug 都与一个基本假设有关,在虚拟机里运行的类型实际上使用的是 raw type,不注意这一点就可能出错。parameterized type vs raw type

  • 不能使用基本类型实例化 type parameters。
  • 动态类型识别只能在 raw type 上工作,也就是 instanceof、Class ==、getClass() 这类操作都只能当做 raw type 工作。
  • 不能创建 parameterized type 的数组(Pair<Sring>[] a = new Pair<String>[1]是不可能的)
  • 但可以赋值到泛型数组,见下一条。
  • 类型参数可以和 vargargs 一起工作,如果使用@safevarargs连警告都不会有。也就是说这样的语句是成立的Pair<String>[] pair = makePairs(pair1, pair2)(这一条破坏了第三条规则)。但泛型数组还是危险的,因为它是协变(covariant)的。泛型数组适合被写,但不适合被读。尽量避免使用它,否则会出现很奇怪的运行时错误
  • 不能初始化 type variables,最常见的非法写法是new T()(但 T t 是很常见的)。一个 workaround 是在使用 T 的地方都使用Class<T>,然后借反射来生成对应的对象。
  • 不能创建泛型数组(T[] a = new T[1])。
  • 泛型类的 type variables 不能在它的 static 上下文里工作。但静态方法自己可以有 type variables。各有各的泛型。
  • 不能抛出和捕获异常类。
  • 巧妙地使用泛型,可以破坏 checked exception 的限制
  • 因为每个泛型方法都有一个兜底的 raw type 方法兜底,如果兜底方法和父类的非泛型方法相冲突(clash),编译会报错。举例,永远不要写boolean equals(T object),因为编译器会擦除出 boolean equals(Object object),制造同签名的方法,连重载都算不上。

泛型能够做什么

  • type variable 可以拿来做目标参数,如T t = get()(T)

通配符(wildcard)

?是通配符。
? extends T 作为一个 type parameter 证明可以在此处读。Pair<? extends Employee>意味着它的子类可以是Pair<Employee>Pair<Manager>
? super T 作为一个 type parameter 证明可以在此处写。Pair<? super Employee>意味着它的子类 可以是Pair<Employee>Pair<Object>

LocalDateChronoLocalDate的子类(顺序就是这样,没有反过来),但 ChronoLocalDate 已然实现了 comparable<ChronoLocalDate>。这时候 LocalDate 的比较方法就应该声明成<T extends Comparable<? super T>>- 这是 comparable 的标准泛型方案。从 A 派生出 B,则 B 的 comparable 方法必须声明为可以支持 super 的类型,这样对 A 的 compare 才能同时兼容 A、B - 而不只是 B,Lists 的 removeIf 方法的谓词同理。(泛型集成对比点 2)

通配符的存在实际上是为了放松“泛型不能支持协变”,而需要让程序员灵活使用多种实际类型做的一个妥协。

举例,Java 8 中的 ArrayList 有个 removeIf 的方法,它的参数是个 predicate,但这个 predicate 的实参可以是,比如 Employee,也可以是 Object(用上了 super)。

Pair<?>是个没用窝囊(wimpy)的类型,它的setFirst(? object) 方法甚至无法被使用(试想,setFirst 怎样确定它的设值是兼容某个类型的?,?实际上近于 ? extends)。 但如果有些场景只是从某类Pair<T>内部读值,那么Pair<?>Pair<T> 更加简洁易读。

通配符捕获

? 不是 type variable,是 wildcard variable。

? a = Pair.getFirst() 是不合法的。
引入一个 T 来捕获通配符,就可以执行这个方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public void <T> swapHelper(Pair<T> pair) {
// 只要不返回这个 a,就不会有编译问题,在方法体内可以随意使用 a。用有值的实例来获取泛型变量。
T a = Pair.getFirst();
}

// 甲骨文的例子
public class WildcardFixed {

void foo(List<?> i) {
fooHelper(i);
}

// Helper method created so that the wildcard can be captured
// through type inference.
private <T> void fooHelper(List<T> l) {
l.set(0, l.get(0));
}

}

在这里,T 捕获了通配符。T 不知道 ? 的具体类型,但知道它是某个确定类型。

编译器只有在确定 T 可以捕获确定的通配符的时候才允许编译通过。例如List<Pair<?>> 多了一个间接层,一个 list 可能有不同的 pair,持有不同的具体类型,编译器不会允许 List<Pair<T>>产生捕获。

另一个例子:

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
public TestClass {

// 任何相关的问题都可以写一个成员类来直接通过反射来实验
private List<? extends Number> listNum;

@Test
@SuppressWarnings("unchecked")
void testGetWildCardType() throws NoSuchFieldException {

final Field fieldNum = TestClass.class.getDeclaredField("listNum");
// genericType几乎必然是 ParameterizedTypeImpl 的实例
final Type genericType = fieldNum.getGenericType();
final ParameterizedType parameterizedType = (ParameterizedType) genericType;
final Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
for (Type actualTypeArgument : actualTypeArguments) {
// wildCard 隐藏在 actualTypeArguments 里,不隐藏在
if (actualTypeArgument instanceof WildcardType) {
final WildcardType wildcardType = (WildcardType) actualTypeArgument;
final Type[] lowerBounds = wildcardType.getLowerBounds();
// 所有的 bound 都是 class
log.info(Arrays.toString(lowerBounds));
final Type[] upperBounds = wildcardType.getUpperBounds();
log.info(Arrays.toString(upperBounds));
}
}

Assertions.assertTrue(true);
}
}

泛型与反射

泛型与 class

String.class 是Class<String>的一个 object。

Class 的 type variable 实际上限制了它方法的种种返回值。

反射能够在擦除后知道些什么

可以知道的以下东西:

  • 一个方法或者类型有个 type parameter T。
  • T 有 super 或者 extends 的 bound。
  • T 有个 wildcard variable。

不可以知道的东西

  • 到底运行时绑定的 type parameter 是什么?

反射的类型 hiarachy

反射的基础类型是 Type 接口,它有五个子类:

  • Class 类,描述具体类型(而不是接口)。我们常见的类型如果本身不带有<>就属于这一类型。对 Java 1.5 以后的泛型,一个 Map.class 和Map<String, Integer>的 class 在求等上完全相等,但唯有引入其他 Type 子类才能说明 String 和 Integer 的存在。
  • TypeVariable 接口,描述类型参数,如 T
  • WildcardType 接口,描述通配符,如 ?。它必然可以找到 up bound(最起码有 Object),不一定有 down bound。
  • ParameterizedType 接口,描述泛型类或接口类型,如Comparable<? super T>- 奇怪,是 T 而不是 String。常见的 ParameterizedType 有 Collection 的 Class 实例。可以简单把 ParameterizedType 理解为 指向 Collection 类的特殊的 class,它携带的 actualParamters 就是String/Integer 的 class,而 WildcardType 的 bounds 就是 extends A 的那个 A 的class。它还有一个 getRawType(也就是 Map),和 ownerType(也就是 Map 之于 Entry)。
  • GenericArray 接口,描述泛型数组如 T[]。这可以被用在获取方法参数上。获取到具体参数以后,可以把它转化为 ParameterizedType,然后获取它携带的 actualParamters。

TypeLiteral

有一个可以捕获多重泛型的实参的方案。

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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68

// 1,当我们拿到一个Class,用Class. getGenericInterfaces()方法得到Type[],也就是这个类实现接口的Type类型列表。
// 2,当我们拿到一个Class,用Class.getDeclaredFields()方法得到Field[],也就是类的属性列表,然后用Field. getGenericType()方法得到这个属性的Type类型。
// 3,当我们拿到一个Method,用Method. getGenericParameterTypes()方法获得Type[],也就是方法的参数类型列表。

/**
* This constructor must be invoked from an anonymous subclass
* as new TypeLiteral<. . .>(){}.
*/
public TypeLiteral()
{
//
Type parentType = getClass().getGenericSuperclass();
if (parentType instanceof ParameterizedType)
{
// 重要:这里得到的 Type 是真的 Class 实例,不过做类型判定不能用 instanceof,只能用 assignableFrom。因为 Class 的自身类型和指代类型是不一样的。当然 Type 也可能是 ParameterizedType 等任意子类型,它们也可以通过 getClass 理解自身的类型。
type = ((ParameterizedType) parentType).getActualTypeArguments()[0];
}
else
throw new UnsupportedOperationException(
"Construct as new TypeLiteral&lt;. . .&gt;(){}");
}

// 上述 api 的例子
public static void main(String[] args) {
List<String> list = new ArrayList<>();
Class<? extends List> firstClazz = list.getClass();
Type genericSuperclass = firstClazz.getGenericSuperclass();
// java.util.AbstractList<E>
System.out.println(genericSuperclass);

ParameterizedType parameterizedSuperType = (ParameterizedType) genericSuperclass;
Type[] actualTypeArguments = parameterizedSuperType.getActualTypeArguments();
// E
Type actualTypeArgument = actualTypeArguments[0];
System.out.println(actualTypeArgument);

// java.util.AbstractCollection<E>
genericSuperclass = firstClazz.getSuperclass().getGenericSuperclass();
System.out.println(genericSuperclass);

parameterizedSuperType = (ParameterizedType) genericSuperclass;
actualTypeArguments = parameterizedSuperType.getActualTypeArguments();
// E
actualTypeArgument = actualTypeArguments[0];
System.out.println(actualTypeArgument);
}

// 但这个方法有一个例外,如:

private static class StringList extends ArrayList<String> {
}

public static void main(String[] args) {
List<String> list = new StringList();
Class<? extends List> firstClazz = list.getClass();
Type genericSuperclass = firstClazz.getGenericSuperclass();
// java.util.ArrayList<java.lang.String>
System.out.println(genericSuperclass);

ParameterizedType parameterizedSuperType = (ParameterizedType) genericSuperclass;
Type[] actualTypeArguments = parameterizedSuperType.getActualTypeArguments();
// class java.lang.String 类型不再是 typevariable
Type actualTypeArgument = actualTypeArguments[0];
System.out.println(actualTypeArgument);
}

// 这个例外下面有用。

运行时捕获类型参数的方法

方法一

1
2
3
4
5
6
7
8
9
10
11
12
public class GenericClass<T> {

private final Class<T> type;

public GenericClass(Class<T> type) {
this.type = type;
}

public Class<T> getMyType() {
return this.type;
}
}

方法二

typetools

方法三

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public abstract class TypeReference<T> implements Comparable<TypeReference<T>>
{
protected final Type _type;

protected TypeReference()
{
Type superClass = getClass().getGenericSuperclass();
if (superClass instanceof Class<?>) { // sanity check, should never happen
throw new IllegalArgumentException("Internal error: TypeReference constructed without actual type information");
}
/* 22-Dec-2008, tatu: Not sure if this case is safe -- I suspect
* it is possible to make it fail?
* But let's deal with specific
* case when we know an actual use case, and thereby suitable
* workarounds for valid case(s) and/or error to throw
* on invalid one(s).
*/
_type = ((ParameterizedType) superClass).getActualTypeArguments()[0];
}
}

这个方法来自于 JDK 5 的作者的博客《super-type-tokens》

  1. abstract class 保证了这个类型必须通过子类确定,这样 getGenericSuperclass 必定会得到一个 ParameterizedType 而不仅仅是一个 GenericType。
  2. implements Comparable<TypeReference<T>> 并不是真的希望子类覆写一个比较方法,而是希望子类型不要实现成一个 raw type。

保证了这两天 _type 一定是一个 concret class。

各种泛型黑魔法

Optional 里通配符转成类型参数

可见@SuppressWarnings("unchecked") java 的基础库里到处都是。

1
2
3
4
5
6
7
8
9
10
/**
* Common instance for {@code empty()}.
*/
private static final Optional<?> EMPTY = new Optional<>();

public static<T> Optional<T> empty() {
@SuppressWarnings("unchecked")
Optional<T> t = (Optional<T>) EMPTY;
return t;
}

确认消费类型的两种方法

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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69

/**
* 验证一个类型是不是另一个类型的参数化子类
* <p>
* A implements B<C>
* 则 isGenericSubClass(A.class, B.class, C.class) 为 true
* 注意,如果存在 D extends C,C extends F
* D 作为第三个参数返回 true,而 F 返回 false
*
* @param subClass 子类型
* @param genericSuperClass 超类型,可以是借口
* @param targetGenericActualParameter 类型实参
* @return 认定结果
*/
public static boolean isGenericSubClass(Class<?> subClass, Class<?> genericSuperClass, Class<?> targetGenericActualParameter) {

if (!genericSuperClass.isAssignableFrom(subClass)) {
return false;
}
final Type genericSuperclass = subClass.getGenericSuperclass();

if (genericSuperclass instanceof ParameterizedType) {
final ParameterizedType parameterizedType = (ParameterizedType) genericSuperclass;
final Type rawType = parameterizedType.getRawType();
// 这里可以确认 Collection 是不是 Collection-而不是 List
final boolean rawTypeMatch = rawType == genericSuperClass;
final Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
// 这两行很重要,基本上能够获取类型实参就靠它了,但我们也只能得到 class
for (Type type : actualTypeArguments) {
if (type instanceof Class) {
if (rawTypeMatch && ((Class<?>) type).isAssignableFrom(targetGenericActualParameter)) {
return true;
}
}
}
}


final Type[] genericInterfaceTypes = subClass.getGenericInterfaces();
for (Type genericInterfaceType : genericInterfaceTypes) {
if (genericInterfaceType instanceof ParameterizedType) {
final ParameterizedType parameterizedType = (ParameterizedType) genericInterfaceType;
final boolean rawTypeMatch = parameterizedType.getRawType() == genericSuperClass;
final Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
// 这两行很重要,基本上能够获取类型实参就靠它了,但我们也只能得到 class
for (Type type : actualTypeArguments) {
if (type instanceof Class) {
if (rawTypeMatch && ((Class<?>) type).isAssignableFrom(targetGenericActualParameter)) {
return true;
}
}
}
}
}
return false;
}

/**
* isGenericSubClass 的 Spring 版本,只能验证第一个 TypeParameter,验证第二个参数必然返回 false
*
* @param subClass 子类型
* @param genericSuperClass 超类型,可以是借口
* @param targetGenericActualParameter 类型实参
* @return 认定结果
*/
public static boolean isParameterized(Class<?> subClass, Class<?> genericSuperClass, Class<?> targetGenericActualParameter) {
final ResolvableType generic = ResolvableType.forClass(subClass).as(genericSuperClass).getGeneric();
return generic.isAssignableFrom(ResolvableType.forClass(targetGenericActualParameter));
}

空泛型数组的拷贝方法

1
2
3
4
5
6
7
8
9
10
11
12
@Override
@SuppressWarnings("unchecked")
public <T> T[] toArray(T[] a) {
int size = size();
if (a.length < size)
return Arrays.copyOf(this.a, size,
(Class<? extends T[]>) a.getClass());
System.arraycopy(this.a, 0, a, 0, size);
if (a.length > size)
a[size] = null;
return a;
}

其他资料

angelikalanger 的 JavaGenericsFAQ