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

以Java 8方式检查对象中包含的空对象和空值,java,arraylist,java-8,optional,Java,Arraylist,Java 8,Optional,如何将此函数重写为更具可选性的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 Illeg

如何将此函数重写为更具可选性的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;
}
public void setMemory(ArrayList内存){
if(内存==null)
抛出新的IllegalArgumentException(“错误:内存对象不能为null”);
if(memory.contains(null))
抛出新的IllegalArgumentException(“错误:内存对象不能包含空值。”);
这个。记忆=记忆;
}

对于我将使用的第一种情况:
对象。requirennoull()


我不认为
Optional
是一种方法,因为
null
是一个非法值。

如果您想坚持
IllegalArgumentException
并且类路径上有番石榴,您可以使用以下方法:

Preconditions.checkArgument(memory != null, 
            "ERROR: memory object can't be null.");
Preconditions.checkArgument(!memory.contains(null), 
            "ERROR: memory object can't contain null value.");
您不能在这里真正使用
可选
,因为您需要针对不同的条件显示不同的错误消息

另一方面,如果只有一条错误消息,则可以执行以下操作:

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

您有一个模式
条件->抛出异常
,可以将其移动到方法:

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;
}

private void checkorelsetrow(布尔条件,供应商仅当您需要两个不同的异常和更多功能样式时,才可以使用我的解决方案。但它看起来很复杂,甚至更长

.map(e->false)
将列表元素(本例中为整数)映射到布尔值,这是
filter()
所需的布尔值


首先,最好使用更通用的列表类型作为输入参数,因此将实现更改为:

public void setMemory(List<Integer> memory) {
    //stuff
}
如果您不能使用guava,但仍然不想使用这种方法,那么您可以编写自己的列表实现,为您执行这种空检查


最后,如果所有这些对您来说都不可能,只需保留您的代码。它很容易阅读,而且每个人都知道您在做什么。从这里的其他答案中可以看出,使用选项可能会非常混乱。

不要使用选项,它们在这里对您没有好处

取而代之的是使用一个更合适的类型来代替ArrayList。在集合中存储整数会产生(un)装箱成本,并且在不允许空值时没有意义

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

  • HPPC集合(我最喜欢,但API与Java集合框架不兼容)
  • 科洛博克
  • Fastutil
  • 所有这些库都为原语提供了列表、映射和其他容器的专门实现。这些实现通常比涉及
    ArrayList
    的任何实现都要快得多(除非ArrayList中的所有整数都足够小,可以放入全局
    Integer
    实例缓存)


    作为一个很好的副作用,使用基本整数的专用列表在默认情况下不允许调用方存储空值。

    一个带选项的行:

    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."));
    }
    
    public void setMemory(ArrayList内存){
    this.memory=Optional.ofNullable(memory).map((a)->Optional.ofNullable(a.contains(null)?null:a).OrelsThrow(()->新的IllegalArgumentException(“错误:内存对象不能包含null值”))).OrelsThrow(()->新的IllegalArgumentException(“错误:内存对象不能为null”);
    }
    
    对于这类情况,我通常避免使用
    可选的
    ,因为它往往会掩盖正在发生的事情

    但首先我要提到的是,原始代码允许调用方保留对包含类的内部字段
    内存的引用。也许您相信调用方不是恶意的,但调用方可能会意外地重用作为参数传递的列表。如果这样做,尽管进行了细致的参数检查,但
    内存
    列表最终可能包含空值。或者,它可能会意外更改,导致其他故障

    解决方案是制作参数列表的防御性副本。简单的方法如下:

    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;
    }
    
    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;
    }
    
    现在可以将其重写为使用
    可选

    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"));
    }
    
    public void setMemory(ArrayList内存){
    this.memory=Optional.ofNullable(内存)
    .map(ArrayList::新建)
    .filter(列表->!list.contains(null))
    .orelsetrow(()->new IllegalArgumentException(“内存为空或包含空”);
    }
    
    与通常的滥用相比:-)的
    Optional
    我经常看到,这一个还不错。这里的链接可以避免创建局部变量,这是一种胜利。逻辑相当简单,特别是如果前脑上有
    可选的
    。然而,我会有点担心在一个月内重新访问这段代码。在说服自己它做了你想做的事情之前,你可能得眯着眼睛看一会儿

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

  • 通常的首选(至少在JDK中)是在这些情况下使用
    NullPointerException
    。对于这些示例,我一直坚持使用
    IllegalArgumentException
    ,因为OP就是这么用的

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


  • 很抱歉添加了另一个答案,但根据对该问题的阅读评论,可能还有更好的方法来更改该方法的签名:将
    ArrayList
    替换为
    IntStream

    public void setMemory(@NonNull IntStream input) {
        Objects.requireNonNull(input);
    
        this.memory = ...; // collect the stream into the storage
    }
    
    原始流不会产生(取消)装箱的成本


    这样,您就不必担心调用者更改您脚下的列表内容,并且可以按照my中的说明为整数选择合适的存储空间(甚至可以懒散地解析流内容!)。

    如果您仍然希望抛出两个不同的自定义异常,我不知道如何将其缩短。我觉得这很好,我更喜欢h
    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;
    }
    
    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"));
    }
    
    public void setMemory(@NonNull IntStream input) {
        Objects.requireNonNull(input);
    
        this.memory = ...; // collect the stream into the storage
    }