JDK17下如何强制方法仅接收不可变List作为参数?
JDK17下强制方法参数为不可变List的实现方式
因为你没法直接用Guava的ImmutableList,这里给你几个可行的方案:
方法一:运行时校验,拒绝可变List
在方法开头加校验逻辑,确保传入的List是不可变的。可以通过尝试执行修改操作(比如add null)来判断——不可变List执行修改会抛出UnsupportedOperationException,如果是List.of()创建的不允许null的不可变List,会抛NullPointerException,这两种情况都说明是不可变集合。
代码示例:
public String topRankingStudentName(List<Student> students) { try { students.add(null); // 能走到这里说明是可变List,直接抛异常 throw new IllegalArgumentException("必须传入不可变List,请用List.of()或Collections.unmodifiableList()创建"); } catch (UnsupportedOperationException | NullPointerException e) { // 是不可变集合,继续执行业务逻辑 } return students.stream() .min(Comparator.comparingInt(Student::rank)) .map(Student::name) .orElse(null); }
也可以直接判断List的类名是否属于JDK内部的不可变集合实现(比如java.util.ImmutableCollections开头的类),但这种方式依赖JDK内部实现,版本变更可能失效,不如上面的通用。
方法二:自定义不可变List包装类,编译时强制
自己写一个不可变的List包装类,把方法参数类型改成这个自定义类,调用者必须将集合转换成该类型才能传入,从编译层面强制不可变性。
先写包装类:
public final class ImmutableListWrapper<T> implements List<T> { private final List<T> delegate; private ImmutableListWrapper(List<T> delegate) { // 内部直接转成JDK原生不可变List,确保底层不可变 this.delegate = List.copyOf(delegate); } // 只实现所有只读方法 @Override public int size() { return delegate.size(); } @Override public boolean isEmpty() { return delegate.isEmpty(); } @Override public boolean contains(Object o) { return delegate.contains(o); } @Override public Iterator<T> iterator() { return Collections.unmodifiableIterator(delegate.iterator()); } @Override public Object[] toArray() { return delegate.toArray(); } @Override public <T1> T1[] toArray(T1[] a) { return delegate.toArray(a); } @Override public boolean containsAll(Collection<?> c) { return delegate.containsAll(c); } @Override public T get(int index) { return delegate.get(index); } @Override public int indexOf(Object o) { return delegate.indexOf(o); } @Override public int lastIndexOf(Object o) { return delegate.lastIndexOf(o); } @Override public ListIterator<T> listIterator() { return Collections.unmodifiableListIterator(delegate.listIterator()); } @Override public ListIterator<T> listIterator(int index) { return Collections.unmodifiableListIterator(delegate.listIterator(index)); } @Override public List<T> subList(int fromIndex, int toIndex) { return new ImmutableListWrapper<>(delegate.subList(fromIndex, toIndex)); } // 所有修改方法直接抛异常 @Override public boolean add(T t) { throw new UnsupportedOperationException("不可变List不支持修改"); } @Override public boolean remove(Object o) { throw new UnsupportedOperationException("不可变List不支持修改"); } @Override public boolean addAll(Collection<? extends T> c) { throw new UnsupportedOperationException("不可变List不支持修改"); } @Override public boolean addAll(int index, Collection<? extends T> c) { throw new UnsupportedOperationException("不可变List不支持修改"); } @Override public boolean removeAll(Collection<?> c) { throw new UnsupportedOperationException("不可变List不支持修改"); } @Override public boolean retainAll(Collection<?> c) { throw new UnsupportedOperationException("不可变List不支持修改"); } @Override public void clear() { throw new UnsupportedOperationException("不可变List不支持修改"); } @Override public T set(int index, T element) { throw new UnsupportedOperationException("不可变List不支持修改"); } @Override public void add(int index, T element) { throw new UnsupportedOperationException("不可变List不支持修改"); } @Override public T remove(int index) { throw new UnsupportedOperationException("不可变List不支持修改"); } // 静态工厂方法,供外部创建实例 public static <T> ImmutableListWrapper<T> of(List<T> list) { return new ImmutableListWrapper<>(list); } }
然后修改你的方法签名:
public String topRankingStudentName(ImmutableListWrapper<Student> students) { return students.stream() .min(Comparator.comparingInt(Student::rank)) .map(Student::name) .orElse(null); }
调用的时候必须这样传:
List<Student> studentList = List.of(new Student("张三", 1, "一中", "东区"), new Student("李四", 2, "二中", "西区")); ImmutableListWrapper<Student> immutableList = ImmutableListWrapper.of(studentList); String topName = topRankingStudentName(immutableList);
这样调用者没办法直接传可变List,必须转成你的不可变包装类,从编译阶段就限制了。
方法三:方法内部创建不可变副本(无需强制调用者,但自身安全)
如果不需要强制调用者传入不可变集合,只是想保证方法内部操作的是不可变的,直接在方法开头用List.copyOf()把传入的List转成不可变副本就行:
public String topRankingStudentName(List<Student> students) { List<Student> immutableStudents = List.copyOf(students); return immutableStudents.stream() .min(Comparator.comparingInt(Student::rank)) .map(Student::name) .orElse(null); }
这种方式最省心,不管调用者传什么,你内部用的都是不可变的,不会受外部修改影响,但没法约束调用者的传入行为。
内容的提问来源于stack exchange,提问作者Arun
相关产品推荐
相关产品推荐

