泛型类声明必须写<T>且紧随类名后;static成员不能引用T;不能new T()或instanceof T;子类继承需指定具体类型或继续泛型化。

泛型类声明时必须写 <T>,不是可选项
Java 泛型类的类型参数声明位置非常严格:它必须紧跟在类名后面、类体之前,用尖括号包裹,比如 class Box<T>。漏掉 <T> 或把它写在继承子句之后(如 class Box extends Object<T>),编译器直接报错 illegal start of type。
- 错误写法:
class Box implements Serializable<T>——<T>不能挂在接口后 - 正确顺序:
class Box<T> implements Serializable - 多个类型参数用逗号分隔:
class Pair<K, V>,不能写成<K V>或<K><V> - 类型参数名只是占位符,用
T、E、K、V是惯例,但写成MyType也合法;不过别用已存在的类名(如String),否则会遮蔽真实类型
static 成员不能引用类级泛型参数
泛型类的 <T> 是实例级别的——每个具体化类型(如 Box<String>、Box<Integer>)在运行时共享同一份字节码,但 static 域/方法属于类本身,不绑定任何具体类型。所以你不能在 static 上下文中使用 T。
- 编译错误:
static T defaultValue;→illegal reference to type parameter T - 也不能写
static void print(T item)—— 方法签名里出现T就违法 - 如果真需要泛型静态行为,得把泛型移到方法上:
static <T> void print(T item),这是静态泛型方法,和类泛型无关 - 常见误踩:想用
static List<T> cache缓存所有类型的实例,实际只能用List<Object>或Map<Class<?>, List<?>>这类擦除后安全的结构
泛型类不能直接 new T(),也不能用 instanceof T
因为类型擦除,JVM 在运行时根本不知道 T 是什么具体类型,所有泛型信息只存在于编译期。所以你无法在泛型类内部安全地构造 T 实例或做精确类型判断。
-
new T()直接编译失败 —— 类型T在字节码里是Object,没有无参构造器保证 -
if (obj instanceof T)语法错误,instanceof右侧必须是具体类型或类型变量(但不能是未绑定的泛型参数) - 绕过方案:传入
Class<T>对象,比如new Box<String>(String.class),然后用clazz.getDeclaredConstructor().newInstance() - 注意
Class<T>本身也有擦除风险:传String.class没问题,但List<String>.class不合法(泛型类字面量不存在),只能用List.class
子类继承泛型类时,要么指定具体类型,要么继续泛型化
继承泛型类不是自由选择:如果你不填具体类型,就必须自己也声明泛型参数,否则编译器无法确认类型关系是否安全。
立即学习“Java免费学习笔记(深入)”;
- 合法:
class StringBox extends Box<String>(闭合类型) - 也合法:
class GenericBox<T> extends Box<T>(传递泛型) - 非法:
class BadBox extends Box—— 看似“原样继承”,实则触发原始类型警告,且丢失类型安全;调用get()返回Object而非T - 更隐蔽的坑:
class IntBox extends Box<Integer>看起来没问题,但如果父类Box<T>有void set(T item),子类重写时不能改成void set(Number item)—— 这会破坏协变性,编译不通过
类型擦除让泛型看起来像语法糖,但它的约束全在编译期强制执行。最常被忽略的是:你以为写了 <T> 就万事大吉,其实每处对 T 的使用都在悄悄接受擦除后的限制——尤其是 static、new、instanceof 和继承这四类场景,几乎必然出错。









