对于这种情况,我通常避免使用Optional
,因为它往往会使事情变得不清晰。
但首先,我想提到的是,原始代码允许调用者保留现在是包含类的内部字段memory
的引用。也许您相信调用者不会恶意操作,但调用者可能会意外地重用作为参数传递的列表。即便进行了细致的参数检查,如果发生这种情况,memory
列表最终可能包含空值。或者,它可能会意外更改,导致其他故障。
解决方案是对参数列表进行防御性拷贝。直接的做法如下:
public void setMemory(ArrayList<Integer> memory) {
if (memory == null)
throw new IllegalArgumentException("memory is null");
List<Integer> temp = new ArrayList<>(memory);
if (temp.contains(null))
throw new IllegalArgumentException("memory contains null");
this.memory = temp;
}
请注意,在检查之前,复制的内容被存储在本地变量
temp
中。显然,在列表检查是否包含空值之前,你不希望将其存储到字段中。但是应该在副本上检查是否包含空值,而不是在参数列表上检查,否则,调用者可能会在检查后但在复制之前修改列表。(是的,这有点多虑。)
如果您不关心确切的异常消息,可以缩短如下:
public void setMemory(ArrayList<Integer> memory) {
List<Integer> temp;
if (memory == null || ((temp = new ArrayList<>(memory)).contains(null)))
throw new IllegalArgumentException("memory is or contains null");
this.memory = temp;
}
现在这段代码可以使用
Optional
进行重写:
public void setMemory(ArrayList<Integer> memory) {
this.memory = Optional.ofNullable(memory)
.map(ArrayList::new)
.filter(list -> ! list.contains(null))
.orElseThrow(() -> new IllegalArgumentException("memory is or contains null"));
}
与我经常看到的
Optional
的常规滥用相比,这个并不太糟糕。这里的链接用于避免创建本地变量,这是一个小小的优势。逻辑相当简单,特别是如果一个人在头脑中有
Optional
。然而,我对重新访问这段代码(比如说一个月后)感到有些担忧。你可能需要花一段时间来凝视它,并使自己相信它做了你打算做的事情。
最后,还有一些通用的风格注释。
通常的偏好(至少在JDK中)是对这些情况使用NullPointerException
。这些示例中我坚持使用IllegalArgumentException
,因为这是OP正在使用的。
我建议使用List<Integer>
代替参数类型和可能的字段类型ArrayList<Integer>
。这将使得在适当的情况下可以使用不可修改的列表(例如,使用JDK 9的List.of
)。
Objects.requireNonNull
是一个Java方法,用于检查传递给它的对象是否为null,如果为null,则会抛出NullPointerException异常。 - Eugene