深入解析Java泛型与继承体系

1. 泛型子类化规则

在Java泛型中,List<String>并不是List<Object>的子类,这与数组的行为完全不同(String[]Object[]的子类)。这种设计是为了保证类型安全。

List<String> strings = new ArrayList<>();
List<Object> objects = strings; // 编译错误!

为什么这样设计?
如果允许这样的赋值,会导致类型安全问题:

objects.add(123); // 如果允许,这里就会把Integer放入String列表
String s = strings.get(0); // 运行时ClassCastException

实践建议

  • 需要向上转型时,使用通配符List<? extends Object>
  • 理解泛型不变性(invariance)原则:G<A>G<B>之间没有继承关系,无论A和B是什么关系

2. 桥方法生成原理

由于类型擦除,编译器需要生成桥方法(bridge method)来保持多态性。这是Java泛型实现的重要机制。

class Node<T> {
    public void setData(T data) { /*...*/ }
}

class MyNode extends Node<Integer> {
    @Override
    public void setData(Integer data) { /*...*/ }
}

编译后,编译器会生成一个桥方法:

class MyNode extends Node {
    public void setData(Integer data) { /*...*/ }
    
    // 桥方法
    public void setData(Object data) {
        setData((Integer)data); // 类型转换
    }
}

实践建议

  • 了解桥方法的存在有助于理解反射时看到的方法数量
  • 桥方法是编译器行为,开发者通常无需直接处理

3. 协变与逆变在泛型中的体现

Java通过通配符实现协变和逆变:

  • 协变(Covariance)<? extends T>,允许读取为T
  • 逆变(Contravariance)<? super T>,允许写入T

图1

示例

// 协变 - 生产者
List<? extends Fruit> fruits = new ArrayList<Apple>();
Fruit f = fruits.get(0); // 可以读取

// 逆变 - 消费者
List<? super Apple> apples = new ArrayList<Fruit>();
apples.add(new Apple()); // 可以写入

PECS原则(Producer-Extends, Consumer-Super):

  • 当只需要从集合读取时,使用? extends T
  • 当只需要向集合写入时,使用? super T
  • 既要读又要写时,不要使用通配符

4. 自限定类型

自限定类型(Self-bounded types)是一种递归类型定义,形式为class A<T extends A<T>>。这种模式常见于构建类型安全的API。

abstract class SelfBounded<T extends SelfBounded<T>> {
    abstract T doSomething();
    
    T chain() {
        return doSomething();
    }
}

class SubClass extends SelfBounded<SubClass> {
    @Override
    SubClass doSomething() {
        return this;
    }
}

应用场景

  1. 构建流畅接口(Fluent API)
  2. 确保方法返回具体子类类型
  3. 实现"模拟自类型"模式

实践建议

  • 在需要方法返回"this"类型但又要保持类型安全时使用
  • 常见于Builder模式的高级实现
  • 注意不要过度使用,会增加代码复杂性

总结

Java泛型与继承体系的交互是类型系统中最复杂的部分之一。理解这些概念有助于:

  1. 设计更安全的API
  2. 避免常见的泛型陷阱
  3. 编写更灵活的泛型代码
  4. 更好地理解Java集合框架的设计

记住关键点:

  • 泛型是不变的(List<String>不是List<Object>的子类)
  • 桥方法是实现多态的关键
  • 协变和逆变通过通配符实现
  • 自限定类型用于构建类型安全的递归API

添加新评论