泛型设计为不可变,根本目的是将类型不安全操作拦截在编译期以规避ArrayStoreException等运行时崩溃;数组协变导致编译通过但运行时可能抛出异常,而泛型不变性禁止List<String>赋值给List<Object>等危险转换,配合类型擦除确保所有校验在编译期完成,只读场景可通过?extends实现安全协变。

泛型设计为不可变(Invariant),根本目的是把类型不安全操作挡在编译期,彻底规避 ArrayStoreException 这类运行时崩溃。
数组协变是运行时风险的源头
Java 数组支持协变:String[] 可赋值给 Object[],Number[] 可赋值给 Object[]。这看似方便,但实际存储时 JVM 仍按数组**真实创建类型**检查——比如 Object[] 引用指向 String[],写入 Integer 就立刻抛 ArrayStoreException。这种“编译通过、运行崩”的模式,违背了强类型语言应有的可预测性。
泛型不变性切断协变带来的写入漏洞
如果泛型也协变,List<String> 就能赋给 List<Object>,那么向后者 add(new Integer(1)) 看似合法,实则破坏了前者只应存 String 的契约。不变性直接禁止这种赋值:
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
- List<String> list = new ArrayList<>();
- List<Object> objList = list; // 编译失败
类型擦除与运行时安全必须协同
泛型在运行时被擦除,只剩原始类型(如 List → List)。若允许协变,运行时无法验证子类型写入是否合法(比如 List<String> 实际是 List,add(Integer) 会静默成功,后续取值才 ClassCastException)。不变性配合擦除,确保所有类型约束都在编译期完成校验,不依赖运行时类型信息兜底。
协变场景可用通配符精准控制读写边界
真有只读需求时,泛型提供 ?extends 机制:
- List<? extends Number> nums = new ArrayList<Integer>(); // ✅ 允许协变读取
- nums.add(new Integer(1)); // ❌ 编译拒绝写入,避免污染

















