collections.checkedlist 通过装饰器模式在运行时拦截 list 的写操作并校验元素类型,弥补泛型擦除缺陷;仅对 add/set/addall 等写操作校验,读操作不校验,不能防反射绕过,适用于边界防御场景。

Java 中 Collections.checkedList 可以在运行时对 List 的元素类型做强制检查,弥补泛型擦除带来的类型安全漏洞。它不是“让泛型在运行时生效”,而是通过包装原始 List,在每次添加、设置或批量操作时动态校验元素类型,防止非法类型混入。
checkedList 的核心作用:运行时类型拦截
泛型在编译后被擦除,JVM 不知道 ListcheckedList 用装饰器模式包装一个原始 List(比如 new ArrayList()),并在 add、set、addAll 等方法中插入类型检查逻辑 —— 若传入对象不是指定类型(或其子类),立即抛出 ClassCastException。
- 只对写操作校验(add/set/addAll/retainAll/removeAll),读操作(get/iterator)不校验,也不影响性能
- 底层 List 本身仍是 raw type,但对外暴露的视图是“类型受控”的
- 不能阻止反射或绕过 API 的非法写入(如直接操作被包装的原始 list),所以需配合良好封装
典型用法:创建并使用 checkedList
假设你接收一个未知类型的 List(比如从外部 API 或旧代码传来),但业务上明确要求它必须只存 Number 实例:
List rawList = getRawList(); // 可能是 new ArrayList() 或其他 raw list List<number> safeList = Collections.checkedList(rawList, Number.class);</number>
之后所有写入都会被校验:
-
safeList.add(42)→ 成功(Integer 是 Number 子类) -
safeList.add("hello")→ 抛出ClassCastException -
safeList.set(0, 3.14)→ 成功(Double 也是 Number)
和泛型声明配合使用的注意事项
声明变量时仍需带上泛型(如 List<string></string>),否则编译器无法提供编译期检查,且 IDE 提示会弱化:
- ✅ 推荐:
List<string> list = Collections.checkedList(new ArrayList(), String.class);</string> - ❌ 避免:
List list = Collections.checkedList(...)—— 失去编译期提示,易引发后续误用 - ⚠️ 注意:
checkedList返回的是List<t></t>,但内部不存储类型参数,仅靠 Class 对象做运行时判断
适用场景与局限性
它适合用于“边界防御”:比如框架接收用户传入的 List,需确保后续处理逻辑不会因类型错乱而崩溃。
- 适合:API 入参校验、配置加载、跨模块数据传递
- 不适合:高频写入的性能敏感路径(有轻微开销)、需要完全杜绝非法类型(仍可被反射绕过)
- 替代方案对比:若需更强约束,可结合自定义不可变包装类 + 构造时遍历校验;若只是临时验证,也可用
stream().allMatch(x -> x instanceof T)做一次性检查
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











