Home > database >  Why Optional.isEmtpy() which checks for NULL is not called isNull()
Why Optional.isEmtpy() which checks for NULL is not called isNull()

Time:09-05

This might be a silly question.

When I am using Optional.isEmpty() like below in the code

Optional<List<String>> optional = Optional.of(new ArrayList<>());

optional.isEmpty(); // only checks if the value is NULL or not.

isEmpty() method is only checking whether the value null or not.

public boolean isEmpty() {
    return value == null;
}

This method name seems awkward for me.

I'm wondering why this method is named isEmpty() and not isNull() since it performs null-check under the hood?

CodePudding user response:

It’s the because the null check is an implementation detail, not what the method logically does. Logically, the method checks whether the optional value is empty or engaged.

Technically, this can be implemented in a number of ways, depending on how the Optional stores its value. JDK 8’s implementation happens to use a reference object with a null check, but other implementations could conceivably do this differently. It’s a mistake to confuse a high-level interface description with its implementation. The entire point of encapsulation is to abstract away implementation details from the user.

CodePudding user response:

You're missing the main point.

Optional is designed to be a container of data which can never be null by itself and which is safe to interact with.

Optional is not intended to be a substitution for null-checks. It's mean to represent a nullable return value and should not be creating in order to hide null-checks or chain methods on it (more on that, see here and here).

If you need to validate that something is not null you can use method Objects#requireNonNull() and it's flavors or explicit null-check.

The idea behind the optional, is that serves as a box for a value, and you can do whole lot of staff with it without caring if the box empty or not. Optional offers you methods to filter to the value inside the box filter(), to replace an empty box with another box or(), to transform the value inside the box map(), etc. And all these actions can be done by chaining the methods fluently without opening the box.

Methods isNull()/isNotNull() are not suitable for the Optional API because they contradict its design goal, which to provide a limited mechanism for representing a nullable return value and a mean of interaction with the target value (around which optional is wrapped) in a simple and fluent way regardless whether its value is null or not.

Note, that checks isPresent() (JDK 8) and isEmpty() (JDK 11) exist as a last resort. Be attentive when you're using them, because most likely you're not leveraging Optional to its full power.

Also, note that wrapping Optional around a Collection or an array doesn't make sense because they are container of data as well (like optional) and might be empty as well, which represents the absents of data.

Doing something like this is redundant and inconvenient.

Optional<List<String>> optional = Optional.of(new ArrayList<>());

When a collection is not null, which should be the case because keeping nullable collections is an antipattern, Collection.isEmpty() is sufficient to check if the data is present. Meanwhile, when you have optional wrapped around collection, the presence of collection inside the optional doesn't means that the actual data is exists.

CodePudding user response:

Here's a different way of implementing Optional which checks emptiness by using different concrete classes for full and empty instances.

public interface MyOptional<T> {
    boolean isEmpty();
    T getOrDefault(T aDefault);

    static <T> MyOptional<T> empty() {
        return new MyOptional<T>() {
            @Override
            public boolean isEmpty() {
                return true;
            }

            @Override
            public T getOrDefault(T aDefault) {
                return aDefault;
            }
        };
    }

    static <T> MyOptional<T> of(T t) {
        return new MyOptional<T>() {
            @Override
            public boolean isEmpty() {
                return false;
            }

            @Override
            public T getOrDefault(T aDefault) {
                return t;
            }
        };
    }

    public static void main(String[] args) {
        MyOptional<String> optString = MyOptional.empty();

        System.out.println(optString.isEmpty());
    }
}

This has a semantic difference from Java's Optional: this can contain a null value in a full instance, while Java's cannot.

  • Related