泛型通配符运行时虽被擦除,但通过反射可获取边界信息:需从ParameterizedType向下解析至WildcardType,调用getUpperBounds()[0]或getLowerBounds()[0]提取上/下界,并依此校验类型兼容性。

泛型通配符在运行时已被擦除,但通过反射仍可获取其原始边界信息——关键在于用对 API,别把 ParameterizedType 当成终点,要继续向下拆解到 WildcardType。
如何从字段或方法参数中提取通配符类型
通配符不会直接暴露在 getGenericType() 返回的顶层类型里,必须逐层解析:
- 先确认目标字段/参数声明的是泛型集合(如
List<? extends Number>),再调用getGenericType() - 若返回值是
ParameterizedType,说明它带实际类型参数,用getActualTypeArguments()[0]取出第一个参数 - 若该参数是
WildcardType实例,才能调用getUpperBounds()或getLowerBounds()获取边界 - 注意:
getUpperBounds()返回数组,即使只有一个上界(如? extends Number),也要取[0];空数组表示无上界(即?)
区分上界与下界的实际含义
通配符不是“模糊类型”,而是有明确读写约束的契约,反射拿到边界后需结合用途判断:
-
? extends T:上界为T,可安全读出T及其子类实例,但禁止写入(除null);反射中getLowerBounds().length == 0且getUpperBounds()[0] == T.class -
? super T:下界为T,允许写入T及其子类,但读出只能当Object;反射中getUpperBounds().length == 0且getLowerBounds()[0] == T.class -
?(无界):上下界数组均为空,此时仅能以Object安全读写,但写入任意对象都可能破坏类型安全
动态调用时避免类型不匹配的坑
反射调用含通配符参数的方法时,不能只看声明类型,还要检查实参是否满足边界约束:
- 若方法形参是
List<? extends CharSequence>,传入ArrayList<String>合法,但ArrayList<Object>不合法(Object不是CharSequence子类) - 可通过
WildcardType.getUpperBounds()拿到CharSequence.class,再用实参泛型的实际类型.isAssignableFrom(上界)做兼容性校验(需先通过反射还原实参的泛型类型) - 对
? super场景,应反向检查:上界是否可被实参类型赋值(即实参类型.isAssignableFrom(下界))
Guice 等框架中构建通配符类型的方法
手动构造 WildcardType 实例不可行(它是接口,无 public 构造器),但 Guice 的 Types 工具类提供了安全封装:
-
Types.subtypeOf(Number.class)→ 对应? extends Number -
Types.supertypeOf(String.class)→ 对应? super String - 这些返回的是
Type,可直接用于Binder.bind(...).toType(...),无需自行处理WildcardType实现细节

















