为什么开发者喜欢泛型?
十年前用ArrayList存数据时,每次取元素都要手动做类型强转,就像在雷区里走路——你永远不知道取出来的到底是String还是Integer。直到Java 5推出泛型,程序员们终于能对着屏幕露出老母亲般的微笑。
- 类型安全防护网:编译器会像安检员一样检查每个放进容器的元素,比如List
绝不允许混入整型数据 - 消灭强制转换:取数据时自动识别类型,从此跟(String) list.get(0)这种危险动作说再见
- 代码复用大师:写一个Box
类就能装下世间万物,从巧克力到宇宙飞船都能打包
场景 | 非泛型时代 | 泛型时代 |
读取集合元素 | 需要显式类型转换 | 自动类型推导 |
编译检查 | 运行时可能报错 | 编译期拦截错误 |
类型推断的魔法
还记得第一次看到List
泛型也有不灵的时候
不过千万别把泛型当万能钥匙,上次有个新手试图用new T创建对象,结果被编译器泼了盆冷水——这活儿泛型还真干不了。
痛点 | 具体表现 |
类型擦除 | 运行时拿不到泛型参数类型 |
基本类型歧视 | 不能用int/long等作为类型参数 |
类型擦除的副作用
泛型在编译后会悄悄擦除类型信息,这就导致两个ArrayList
- 不能直接创建泛型数组,得用(T[]) new Object[size]这种曲线救国的方式
- 静态变量共享问题:所有Box
实例共享同一个静态成员变量 - 方法重载的陷阱:print(List
) 和print(List) 会被认为是同一个方法
泛型世界的生存法则
遇到泛型数组的难题时,老手们会祭出@SuppressWarnings("unchecked")这个护身符。处理通配符边界问题时,记住 extends T>是生产者, super T>是消费者——这个PECS原则(Producer Extends, Consumer Super)可是《Effective Java》里的经典法则。
虽然不能直接用基本类型,但Integer和int之间的自动装箱拆箱机制,让代码写起来就像在沙滩上捡贝壳一样自然。只是性能敏感的场景还是得谨慎,毕竟频繁的装箱操作会让GC(垃圾回收)忙得团团转。
当泛型遇上继承
有个经典面试题:为什么List
不过别灰心,通配符就像瑞士军刀一样解决了部分问题。List extends Number>可以接受List
夜深人静调试泛型代码时,可能会在反射API里碰壁——getGenericType返回的参数化类型信息,在运行时就像薛定谔的猫,既存在又不存在。这时候《Java Generics and Collections》书里的技巧就派上用场了,通过保存类型令牌(Type Token)的方式能曲线获取类型信息。