以Java 8方式检查对象中包含的null对象和null值

时间:2017-11-28 09:20:59

标签: java arraylist java-8 optional

如何使用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;
}

8 个答案:

答案 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。但是,我有点担心在一个月内重新访问此代码。你可能不得不眯着眼睛看一下,然后说服自己做了你打算做的事。

最后,一些一般风格的评论。

  1. 通常的偏好(至少在JDK中)是针对这些情况使用NullPointerException。我已经坚持IllegalArgumentException这些例子,因为这是OP正在使用的内容。

  2. 我建议使用List<Integer>代替ArrayList<Integer>作为参数类型,可能还有字段类型。这样可以在适当的情况下使用不可修改的列表(例如,使用JDK 9&#39; List.of)。

答案 4 :(得分:1)

不要使用Optionals,他们不会在这里受益。

而是使用更合适的类型代替ArrayList。在集合中存储整数会导致(联合国)拳击成本,并且在不允许空值时没有意义。

很少有可能的集合库,可以更好地满足您的需求:

  1. HPPC集合(我最喜欢的,但API与Jav​​a Collection框架不兼容)
  2. Koloboke
  3. Fastutil
  4. 所有这些库都为基元提供了列表,地图和其他容器的专门实现。这些实现通常比涉及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());