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圣经

全部评论

相关推荐

评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务