集合封装需通过职责收敛、接口抽象和行为约束实现可控操作,如BookList内部持私有List、暴露addBook等语义方法并校验参数,返回不可修改视图,统一用接口声明,内聚副作用逻辑,构造器严格校验初始化数据。

集合封装不是把 List 或 Map 包一层就完事,而是通过职责收敛、接口抽象和行为约束,让集合的增删查改变成可控、可校验、可替换的操作。对控制台项目(比如库存管理、日志聚合、用户批量操作)来说,直接暴露 ArrayList 或 HashMap,等于把数据结构的脆弱性全摊在调用方面前——一处 add(null) 可能引发后续空指针,一个没校验的 remove 会漏删,硬编码的容量值还会随业务增长变成技术债。
把集合操作收进专用类,不暴露原始容器
别让 BookList 直接 extends ArrayList
- 定义 BookList 类,内部持有一个 private final List
books = new ArrayList(); - 只暴露业务语义方法:addBook(Book b)、findByName(String name)、borrowByIsbn(String isbn),每个方法内做校验(如 ISBN 非空、书名不为空字符串)
- 不提供 getBooks() 返回原始 List;需要遍历?提供不可修改视图:public List
snapshot() { return Collections.unmodifiableList(books); }
用接口声明,避开具体实现绑定
变量、参数、返回值统一用 Collection 或 List 接口,而不是 ArrayList 或 LinkedList:
- 写 List
books = new ArrayList(); 而非 ArrayList books = new ArrayList(); - 方法签名用 public void importBooks(Collection
newBooks),而非 ArrayList - 这样未来要换成 CopyOnWriteArrayList(多读少写场景)或自定义分页缓存集合,只需改构造,调用方完全无感
封装集合的“副作用逻辑”,比如计数、去重、状态联动
集合常伴随隐式规则:加一本书,库存总数要+1;删一条日志,最近操作时间要更新;借出时自动设 isBorrowed=true 并记录时间。这些不该散落在 Main 或 Service 里:
- 在 BookList.addBook() 内部同步更新 totalBookCount 字段,并触发 onBookAdded 事件(可为空实现)
- 用 Set
做去重底座,但对外仍提供 addBook() 方法——重复添加时静默忽略 or 抛定制异常,由类自己决定策略 - 避免外部代码反复调用 books.contains(b) 或手写 for 循环查重,把这类逻辑收进 findOrAdd()、upsert() 等语义化方法
构造与初始化也走封装通道,杜绝脏数据入口
new BookList() 不该是空壳,也不该允许传入带 null 元素的原始 List:
- 提供带校验的构造器:BookList(Collection
initial) { this.books.addAll(filterNullAndValidate(initial)); } - 所有校验逻辑(如 ISBN 格式、价格非负)只写在 addBook() 和构造器调用的私有校验方法里,不分散
- 禁止提供 public void setBooks(List
books) —— 这等于开了后门,绕过全部约束

















