18.1.7 泛型擦除机制与通配符使用
1. 泛型基础概念
1.1 泛型的定义和作用
泛型是JDK 1.5引入的特性,允许在定义类、接口和方法时使用类型参数,提供编译时类型安全检查。
// 不使用泛型的问题 public class WithoutGenerics { public void demonstrateProblems() { List list = new ArrayList(); list.add("字符串"); list.add(123); // 可以添加不同类型 // 运行时类型转换异常 for (Object obj : list) { String str = (String) obj; // ClassCastException System.out.println(str); } } } // 使用泛型解决问题 public class WithGenerics { public void demonstrateSolution() { List<String> list = new ArrayList<>(); list.add("字符串"); // list.add(123); // 编译错误,类型不匹配 // 无需类型转换 for (String str : list) { System.out.println(str); } } }
1.2 泛型的语法形式
// 泛型类 public class GenericClass<T> { private T data; public GenericClass(T data) { this.data = data; } public T getData() { return data; } public void setData(T data) { this.data = data; } } // 泛型接口 public interface GenericInterface<T, R> { R process(T input); } // 泛型方法 public class GenericMethods { // 静态泛型方法 public static <T> void swap(T[] array, int i, int j) { T temp = array[i]; array[i] = array[j]; array[j] = temp; } // 实例泛型方法 public <T extends Comparable<T>> T findMax(T[] array) { if (array == null || array.length == 0) { return null; } T max = array[0]; for (int i = 1; i < array.length; i++) { if (array[i].compareTo(max) > 0) { max = array[i]; } } return max; } }
2. 类型擦除机制详解
2.1 类型擦除的概念
类型擦除是Java泛型实现的核心机制,编译器在编译时会移除所有泛型类型信息,在字节码中只保留原始类型。
public class TypeErasureDemo { public void demonstrateErasure() { // 编译前 List<String> stringList = new ArrayList<String>(); List<Integer> integerList = new ArrayList<Integer>(); // 编译后(字节码中) // List stringList = new ArrayList(); // List integerList = new ArrayList(); // 运行时类型相同 System.out.println(stringList.getClass() == integerList.getClass()); // true System.out.println(stringList.getClass().getName()); // java.util.ArrayList } // 泛型类的擦除 public static class GenericClass<T> { private T value; public void setValue(T value) { this.value = value; } public T getValue() { return value; } } public void demonstrateClassErasure() { GenericClass<String> stringClass = new GenericClass<>(); GenericClass<Integer> integerClass = new GenericClass<>(); // 运行时类型相同 System.out.println(stringClass.getClass() == integerClass.getClass()); // true } }
2.2 擦除规则
public class ErasureRules { // 规则1: 无界类型参数擦除为Object public class UnboundedGeneric<T> { private T value; // 擦除后变为 Object value; public T getValue() { // 擦除后变为 Object getValue() return value; } } // 规则2: 有界类型参数擦除为第一个边界 public class BoundedGeneric<T extends Number> { private T value; // 擦除后变为 Number value; public T getValue() { // 擦除后变为 Number getValue() return value; } } // 规则3: 多个边界时擦除为第一个边界 public class MultipleBounds<T extends Number & Comparable<T>> { private T value; // 擦除后变为 Number value; public T getValue() { // 擦除后变为 Number getValue() return value; } } // 规则4: 接口作为第一个边界 public class InterfaceFirst<T extends Comparable<T> & Serializable> { private T value; // 擦除后变为 Comparable value; public T getValue() { // 擦除后变为 Comparable getValue() return value; } } }
2.3 桥接方法
public class BridgeMethodDemo { // 父类 public static class Parent<T> { public T getValue() { return null; } public void setValue(T value) { // 设置值 } } // 子类 public static class Child extends Parent<String> { @Override public String getValue() { // 重写方法 return "Child Value"; } @Override public void setValue(String value) { // 重写方法 System.out.println("Setting value: " + value); } } public void demonstrateBridgeMethod() throws Exception { Child child = new Child(); // 获取所有方法 Method[] methods = Child.class.getDeclaredMethods(); System.out.println("Child类的所有方法:"); for (Method method : methods) { System.out.printf("方法: %s, 返回类型: %s, 是否为桥接方法: %s%n", method.getName(), method.getReturnType().getSimpleName(), method.isBridge()); } // 编译器会生成桥接方法: // public Object getValue() { return getValue(); } // 桥接方法 // public void setValue(Object value) { setValue((String)value); } // 桥接方法 } public static void main(String[] args) throws Exception { new BridgeMethodDemo().demonstrateBridgeMethod(); } }
3. 通配符详解
3.1 通配符的类型
public class WildcardTypes { // 1. 无界通配符 ? public void unboundedWildcard() { List<?> list = new ArrayList<String>(); // list.add("test"); // 编译错误,不能添加元素(除了null) list.add(null); // 可以添加null Object obj = list.get(0); // 只能以Object类型读取 System.out.println("读取到: " + obj); } // 2. 上界通配符 ? extends T public void upperBoundedWildcard() { List<? extends Number> numbers = new ArrayList<Integer>(); // numbers.add(123); // 编译错误,不能添加元素 // numbers.add(12.3); // 编译错误,不能添加元素 numbers.add(null); // 只能添加null Number num = numbers.get(0); // 可以以Number类型读取 System.out.println("读取到: " + num); } // 3. 下界通配符 ? super T public void lowerBoundedWildcard() { List<? super Integer> numbers = new ArrayList<Number>(); numbers.add(123); // 可以添加Integer及其子类型 numbers.add(456); // numbers.add(12.3); // 编译错误,Double不是Integer的子类型 Object obj = numbers.get(0); // 只能以Object类型读取 System.out.println("读取到: " + obj); } }
3.2 PECS原则
Producer Extends, Consumer Super - 生产者使用extends,消费者使用super
public class PECSPrinciple { // Producer - 从集合中读取数据(生产数据给你) public v
剩余60%内容,订阅专栏后可继续查看/也可单篇购买
Java面试圣经 文章被收录于专栏
Java面试圣经,带你练透java圣经