【Java实战经验】泛型-类型灵活使用与限制
欢迎来到啾啾的博客🐱。
录学习点滴,分享工作思考和实用技巧,偶尔也分享一些杂谈💬。
欢迎评论交流,感谢您的阅读😄。
泛型
effective-java:29.优先考虑泛型 30.优先使用泛型方法
什么时候需要泛型呢?当需要限制类型,但又需要类型灵活不需要强制转换的场景。
泛型提供了Java常见异常ClassCastException的一个解法。即通过泛型约束对象类型返回,让强制类型转换时更安全、可读,也为编译器提供类型检查依据。
具体来说,泛型使用类型参数 Type Parameter,可以在编译时就制定并检查类型。
泛型实现机制
泛型的实现机制为类型擦除(Type Erasure)。
编译时编译器利用泛型信息进行类型检查,字节码生成时泛型信息会被擦除,无参<T>
和有参<T extend ?>
替换成上界。
当从泛型类中获取数据时,编译器会自动插入一个强制类型转换字节码指令。
无参泛型
以RouYi项目为例,无参泛型在项目中常见于通用Result类。
无参泛型T较Object在使用上的差异在于无参类型获取元素不需要进行强制类型转换,而是编译器进行了类型转换。可读性更高。
// 使用Object的不便
public class ObjectBox {private Object item;public void set(Object item) {this.item = item;}public Object get() {return item;}public static void main(String[] args) {ObjectBox box = new ObjectBox();box.set("Hello");box.set(123);//覆盖// 需要强制转换,且有风险Object retrieved = box.get();if (retrieved instanceof String) {String content = (String) retrieved; // 需要强制转换System.out.println(content.toUpperCase());} else if (retrieved instanceof Integer) {Integer num = (Integer) retrieved; // 需要强制转换System.out.println(num * 2); }// 如果不小心写错了转换类型,就会出问题String oops = (String) box.get(); // 如果 box.get() 返回的是 123, 这里会抛出 ClassCastException}
}// 使用泛型可以制定类型public class TBox<T> {private T item;public void set(T item) {this.item = item;}public T get() {return item;}public static void main(String[] args) {// 实例化时制定参数类型TBox<String> tBox = new TBox();tBox.set("hello");tBox.set(123);// 编译错误!类型不匹配,无法将 int (Integer) 赋值给 StringTBox<Integer> intBox = new TBox<>(); // 指定 T 为 IntegerintBox.set(42); // OK}}
以上案例也可以解答为什么不建议使用原生类型(不带类型参数的泛型类型),如使用TBox时这样使用
TBox tbox = new TBox();
// 类似的还有
List lsit = new ArrayList<>();
这样泛型就没有类型检查约束、强制安全的意义了。
有参泛型
有参泛型有一个PECS法则:“Producer Extends, Consumer Super”。当你的泛型结构主要用于产出 (读取) T 类型的实例时,使用 ? extends T;当它主要用于消费 (写入) T 类型的实例时,使用 ? super T。如果既要读也要写,就不要用通配符,直接用 T。
上界通配符 ? extends T
表示“任何 T 或 T 的子类”。当你需要只从一个泛型集合中获取元素时,使用 extends。
List<? extends Number> numbers;//上界通配符可以让读取时类型安全。// 向上转型
for (Number num : numbers) { // 安全地读取为 Numbersum += num.doubleValue();
}
下界通配符 ? super T
表示“任何 T 或 T 的父类”。当你需要向一个泛型集合中添加 T 类型的元素时,使用 super。
List<? super Integer> list;// 子类可赋值给父类
list.add(Integer.valueOf(10));
PECS利用了Java的向上转型(Upcasting)和向下转型(Downcasting)的概念,算是一种高级应用和体现。
泛型总结
两个点:
- 不要使用原生类型
- PECS:Producer Extends, Consumer Super