Check for null object and null value contained in object in Java 8 way

Jump3r :

how to rewrite this function to be more Java 8 with Optionals? Or should I just leave it as it is?

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;
}
Stuart Marks :

I usually avoid Optional for such cases as it tends to obscure what's going on.

But first I'd like to mention that the original code lets the caller retain a reference to what is now an internal field memory of the containing class. Maybe you trust your callers not to be malicious, but the caller might accidentally reuse the list passed as an argument. If it does, despite the meticulous argument checking, the memory list might end up containing nulls after all. Or, it could change unexpectedly, leading to other malfunctions.

The solution is to make a defensive copy of the argument list. The straightforward way to do this is as follows:

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

Note that the copy is made and stored in a local variable temp prior to being checked. Obviously, you don't want to store into the field before the list is checked for containing nulls. But the check for containing nulls should be done on the copy, not on the argument list, otherwise, the caller could modify the list after the check but before the copy. (Yes, this is being paranoid.)

If you don't care about the exact exception message, this could be shortened as follows:

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

Now this could be rewritten to use 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"));
}

Compared to the usual abuses :-) of Optional I see frequently, this one isn't too bad. The chaining here serves to avoid creation of a local variable, which is a bit of a win. The logic is fairly straightforward, especially if one has Optional on the forebrain. However, I'd be somewhat concerned about revisiting this code in, say, a month. You'd probably have to squint at it a while before convincing yourself it does what you intended it to do.

Finally, a couple general style comments.

  1. The usual preference (at least in the JDK) is to use NullPointerException for these cases. I've stuck with IllegalArgumentException for these examples because that's what the OP is using.

  2. I'd recommend using List<Integer> instead of ArrayList<Integer> for the argument type and possibly the field type. This will enable the use of unmodifiable lists in situations where it's appropriate (e.g., using JDK 9's List.of).

Guess you like

Origin http://10.200.1.11:23101/article/api/json?id=433323&siteId=1