1. 为什么我们需要泛型与通配符
第一次在Java代码中看到List<String>这种写法时,我完全不明白尖括号里的东西有什么用。直到某天我写了个方法要处理不同类型的List,才发现没有泛型的代码有多可怕。想象一下,你从List里取出一个对象,每次都要做类型转换,还要担心ClassCastException——这就是Java 5之前程序员们的日常。
泛型(Generics)本质上是一种参数化类型机制。就像方法可以有参数一样,类型也可以有参数。List<E>中的E就是类型参数,它让编译器能在编译时检查类型安全。2004年引入的这个特性,直接解决了集合框架最头疼的类型安全问题。根据Oracle官方调查,泛型将集合相关的运行时错误减少了约40%。
但泛型有个天生的限制——它太"严格"了。比如你想写个方法打印任何List的内容,用List<Object>作为参数类型的话,传入List<String>反而会编译报错。这就是通配符(Wildcards)要解决的问题,那个神秘的问号?能让类型系统既保持安全又足够灵活。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 泛型的实现原理与类型擦除
2.1 类型擦除的幕后机制
Java泛型最让人困惑的点莫过于类型擦除(Type Erasure)。编译器在处理List<String>时,实际上会把类型参数String"擦除",最终生成的字节码里只有原始的List。这个设计是为了保持向后兼容性,但也带来了一些限制。
通过javap反编译可以看到:
java复制List<String> list = new ArrayList<>();
list.add("hello");
String s = list.get(0);
编译后会变成:
java复制List list = new ArrayList();
list.add("hello");
String s = (String)list.get(0); // 自动插入的类型转换
关键理解:泛型只在编译阶段起作用,运行时所有泛型类型都变为原始类型。这就是为什么你不能用
new T()创建泛型实例,也无法用instanceof检查泛型类型。
2.2 桥方法的神秘面纱
当泛型遇到继承时,编译器会生成一些"桥方法"来保持多态性。比如:
