副标题 / 摘要
很多人困惑为什么 List 不能当作 List。本文用类型安全的角度解释协变与逆变。
目标读者
- 学习泛型与类型系统的开发者
- 需要写类型安全 API 的工程师
- 做语言与框架设计的人
背景 / 动机
如果泛型随意协变,会引发类型不安全。
理解协变/逆变能帮助你正确设计接口与集合使用方式。
核心概念
- 协变:子类型关系在泛型中保留
- 逆变:子类型关系方向相反
- 不变:泛型类型不随子类型变化
实践指南 / 步骤
- 只读集合可用协变
- 只写集合可用逆变
- 读写同时存在时保持不变
- 用通配符或泛型参数表达意图
可运行示例
import java.util.List;
class Animal {}
class Cat extends Animal {}
public class VarianceDemo {
public static void main(String[] args) {
List<Cat> cats = List.of(new Cat());
List<? extends Animal> animals = cats; // 协变:只读
Animal a = animals.get(0);
System.out.println(a.getClass().getSimpleName());
}
}
解释与原理
如果允许 List 当作 List,就可能把 Dog 放进去,破坏类型安全。
因此多数语言让泛型默认不变,需要明确声明协变/逆变。
常见问题与注意事项
协变集合能写入吗?
不能,通常只能读。逆变集合能读取吗?
读取会丢失具体类型信息。为什么这么复杂?
为了在灵活性与类型安全之间取得平衡。
最佳实践与建议
- API 设计先区分“只读”与“只写”
- 对外暴露尽量使用协变接口
- 对内实现保持不变类型
小结 / 结论
泛型协变与逆变是类型安全的代价,也是接口设计的基础。
理解它能让你写出更安全的泛型 API。
参考与延伸阅读
- Java Generics and Collections
- Kotlin Variance
元信息
- 阅读时长:6~8 分钟
- 标签:泛型、类型系统
- SEO 关键词:协变, 逆变, 泛型
- 元描述:解释泛型协变/逆变与类型安全。
行动号召(CTA)
检查你项目中的泛型接口,看看是否能更明确地表达读写意图。