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

Jum*_*p3r 13 java arraylist optional java-8

如何使用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;
}
Run Code Online (Sandbox Code Playgroud)

And*_*lko 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;
}
Run Code Online (Sandbox Code Playgroud)

如果不会更改异常的类型,则仅传递异常消息是合理的(感谢@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;
}
Run Code Online (Sandbox Code Playgroud)

  • 如果OP真的想改变代码,我更喜欢这个答案.示例中的null检查足够好,但是这种方法可能被认为更具表现力,尤其是在整个应用程序中使用它时.看看试图用选项做某些事情的答案,我并没有真正看到太多的好处,因为有些检查甚至不是空检查(如示例中的包含检查). (2认同)
  • 如果OP想要在两种情况下抛出相同类型的Exception,您也可以将错误消息传递给方法而不是供应商.现在,它不比OP具有的更短或更易读,只需一行. (2认同)

Eug*_*ene 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.");
Run Code Online (Sandbox Code Playgroud)

您不能真正使用Optional此处,因为您需要针对不同条件的不同错误消息.

如果您可以在另一方面收到单个错误消息,则可以执行以下操作:

this.memory = Optional.ofNullable(memory)
            .filter(x -> !x.contains(null))
            .orElseThrow(() -> new IllegalArgumentException(
                         "memory object is null or contains null values"));
Run Code Online (Sandbox Code Playgroud)

  • 我不相信有两个不同的错误消息的要求(如果有,那么它应该重新考虑)所以我认为你的`Optional`示例是表达这一点的最佳方式. (2认同)

dev*_*ull 6

对于第一种情况,我会使用: Objects.requireNonNull().

我不认为Optionalnull是一种非法价值的方式.

  • 我总是喜欢一种异常类型,它不需要解析消息来找出发生的事情,即`NullPointerException`.可能值得为OP的用例添加实际示例,即`Objects.requireNonNull(内存,"错误:内存对象不能为空.");`和`memory.forEach(o - > Objects.requireNonNull(o ,"错误:内存对象不能包含空值."));`哪个imho表明,与巴洛克式"可选"(ab)使用相比,这些也是最简洁和直接的结构.不谈性能差异...... (5认同)

Stu*_*rks 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;
}
Run Code Online (Sandbox Code Playgroud)

请注意,复制是在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;
}
Run Code Online (Sandbox Code Playgroud)

现在可以重写使用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"));
}
Run Code Online (Sandbox Code Playgroud)

与通常的滥用相比:-) Optional我经常看到,这个并不算太糟糕.这里的链接用于避免创建局部变量,这有点胜利.逻辑是相当简单的,特别是如果一个人Optional在前脑上.但是,我有点担心在一个月内重新访问这段代码.你可能不得不眯着眼睛看一下,然后说服自己做了你打算做的事情.

最后,一对一般的风格评论.

  1. 通常的偏好(至少在JDK中)是NullPointerException用于这些情况.我坚持使用IllegalArgumentException这些例子,因为那是OP正在使用的.

  2. 我建议使用List<Integer>而不是ArrayList<Integer>参数类型和可能的字段类型.这将允许在适当的情况下使用不可修改的列表(例如,使用JDK 9 List.of).

  • 当你遵循建议使用`NullPointerException`并且不关心消息时,它就像`memory = new ArrayList <>(memory);/*(如果内存为空则复制并抛出NPE)*/memory.的forEach(对象:: requireNonNull); this.memory =记忆;`.哦,值得一提的是参数应该更喜欢抽象类型,即`List`而不是`ArrayList`,特别是当我们创建一个具有我们想要的类型`ArrayList`的防御性副本时. (2认同)