如何使用Optionals将此函数重写为更多Java 8?或者我应该保持原样?
public void setMemory(ArrayList<Integer> memory) {
if (memory == null)
throw new IllegalArgumentException("ERROR: memory object can't be null.");
if (memory.contains(null))
throw new IllegalArgumentException("ERROR: memory object can't contain null value.");
this.memory = memory;
}
答案 0 :(得分:8)
你有一个模式condition -> throw an exception
可以转移到方法:
private void checkOrElseThrow(boolean condition, Supplier<? extends RuntimeException> exceptionSupplier) {
if (condition) {
throw exceptionSupplier.get();
}
}
public void setMemory(List<Integer> memory) {
checkOrElseThrow(memory == null, () -> new IllegalArgumentException("message #1"));
checkOrElseThrow(memory.contains(null), () -> new IllegalArgumentException("message #2"));
this.memory = memory;
}
如果不会更改异常的类型,则仅传递异常消息(感谢@tobias_k指出)是合理的:
private void checkOrElseThrow(boolean condition, String exceptionMessage) {
if (condition) {
throw new IllegalArgumentException(exceptionMessage);
}
}
public void setMemory(List<Integer> memory) {
checkOrElseThrow(memory == null, "message #1");
checkOrElseThrow(memory.contains(null), "message #2");
this.memory = memory;
}
答案 1 :(得分:7)
如果你想坚持IllegalArgumentException
和你在类路径上有guava,你可以使用它:
Preconditions.checkArgument(memory != null,
"ERROR: memory object can't be null.");
Preconditions.checkArgument(!memory.contains(null),
"ERROR: memory object can't contain null value.");
由于您需要针对不同条件的不同错误消息,因此您无法在此处使用Optional
。
另一方面,如果你没有单一的错误信息,你可以这样做:
this.memory = Optional.ofNullable(memory)
.filter(x -> !x.contains(null))
.orElseThrow(() -> new IllegalArgumentException(
"memory object is null or contains null values"));
答案 2 :(得分:6)
对于我会使用的第一种情况:
Objects.requireNonNull()
。
我认为Optional
是一种方式,因为null
是非法的价值。
答案 3 :(得分:5)
我经常避免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&#39; List.of
)。
答案 4 :(得分:1)
不要使用Optionals,他们不会在这里受益。
而是使用更合适的类型代替ArrayList。在集合中存储整数会导致(联合国)拳击成本,并且在不允许空值时没有意义。
很少有可能的集合库,可以更好地满足您的需求:
所有这些库都为基元提供了列表,地图和其他容器的专门实现。这些实现通常比涉及ArrayList<Integer>
的任何实现快得多(除非ArrayList中的所有整数都小到足以适应全局Integer
实例缓存)。
作为一个很好的副作用,使用专用的原始整数列表将不允许调用者默认存储空值。
答案 5 :(得分:1)
带选项的一个班轮:
public void setMemory(ArrayList<Integer> memory) {
this.memory = Optional.ofNullable(memory).map((a) -> Optional.ofNullable(a.contains(null) ? null : a).orElseThrow(() -> new IllegalArgumentException("ERROR: memory object can't contain null value."))).orElseThrow(() -> new IllegalArgumentException("ERROR: memory object can't be null."));
}
答案 6 :(得分:1)
很抱歉再添加一个答案,但根据阅读问题的评论,可能有更好的方法来更改方法的签名:将ArrayList<Integer>
替换为IntStream
:
public void setMemory(@NonNull IntStream input) {
Objects.requireNonNull(input);
this.memory = ...; // collect the stream into the storage
}
原始流不会导致(非)拳击费用。
通过这种方式,您不必担心调用者会更改您脚下的列表内容,并且可以按照other answer中的说明为整数选择合适的存储空间(甚至可以解析流内容懒洋洋地!)。
答案 7 :(得分:-1)
只有在需要两个不同的异常和更多功能样式时,才可以使用我的解决方案。但它看起来很复杂甚至更长。
.map(e -> false)
将list的元素(本例中为整数)映射到filter()
所需的布尔值。
this.memory = Optional.ofNullable(memory)
.orElseThrow(() -> new IllegalArgumentException("ERROR: memory object can't be null."))
.stream()
.filter(element ->
Optional.ofNullable(element)
.map(e -> true)
.orElseThrow(
() -> new IllegalArgumentException("ERROR: memory object can't contain null value.")))
.collect(Collectors.toList());