1

Let's say we have a class Product which has some nullable fields that are allowed to be null, for example, quantityAvailable and quantityReserved.

I may want to validate these fields and throw an error, but since they are allowed to be null, I have, firstly, to check for null:

if (product.getQuantityAvailable() != null && product.getQuantityReserved() != null) {
  if (product.getQuantityAvailable() < product.getQuantityReserved()) {
    System.out.println("Error");
  }
}

When you have many nullable fields and many null-checks, this becomes tedious. Even with ofNullable it doesn't get any better.

Optional.ofNullable(product.getQuantityAvailable())
    .ifPresent((available) -> {
          Optional.ofNullable(product.getQuantityReserved())
              .ifPresent((reserved) -> {
                    if (available < reserved) {
                      System.out.println("Error");
                    }
                  });
        });

Ideally there would exist some annotation that would check for nulls and skip if any were found, but obviously this wouldn't work.

if (@Nullable product.getQuantityAvailable() < @Nullable product.getQuantityReserved()) {
  System.out.println("Error");
}

Is there a solution to avoid the boilerplate above?

7
  • Here is a nice article that explains what you can do to avoid null checks: baeldung.com/java-avoid-null-check Commented Aug 12, 2022 at 11:29
  • @AlexanderIvanchenko there are no Optional fields here. I meant "optional" as in "not required", not the Java class. I explain this at the top of my post. The problem here is that some fields in my model are not required, and can be null in the database. Commented Aug 12, 2022 at 12:40
  • @BoškoBezik Hey I came across that article while searching but unfortunately that's for a different case where you want a value to actually not be null, whereas in my case null is allowed. Commented Aug 12, 2022 at 12:44
  • @AlexanderIvanchenko You said "you make these fields Optional in the first place" in your other comment, not me. I never said I had Optional fields. This was something you said. Commented Aug 12, 2022 at 12:46
  • Your best option might be to use a language that has null-safety built in, such as Kotlin. That would allow you to write product?.let { quantityAvailable > quantityRequired }, which still has the same null check it's just easier to read and write. Commented Aug 12, 2022 at 12:57

3 Answers 3

2

Firstly, there's nothing wrong with explicit null-checks.

Secondly, Optional.ofNullable() is not meant for validation. By hiding a null-check with it, you're obscuring what the code does and making it harder to read.

Here's a quote from the answer by @StuartMarks, Java and OpenJDK developer, regarding what Optional is meant to be used for:

The primary use of Optional is as follows:

Optional is intended to provide a limited mechanism for library method return types where there is a clear need to represent "no result," and where using null for that is overwhelmingly likely to cause errors.

A typical code smell is, instead of the code using method chaining to handle an Optional returned from some method, it creates an Optional from something that's nullable, in order to chain methods and avoid conditionals.

Also have a look at this answer by Stuart Marks "Should Optional.ofNullable() be used for null check?".

Using Optional.ofNullable() to avoid null-checks is an antipattern. You shouldn't create Optional in order to validate an object that can be null

I see nothing that can be considered to be "bad" in this snippet from your question. Yes, it's verbose, it's a price for allowing nullable fields.

if (product.getQuantityAvailable() != null && product.getQuantityReserved() != null) {
    if (product.getQuantityAvailable() < product.getQuantityReserved()) {
        System.out.println("Error");
    }
}

If you want to reduce the level of nesting, here's a null-safe alternative with Objects.compare():

if (Objects.compare(product.getQuantityAvailable(), 
                    product.getQuantityReserved(), 
                    Comparator.nullsFirst(Comparator.naturalOrder())) < 0) {
    // perform the required action
    throw new MyCustomException("a message helpful in debuging here");
}

When you have a lot of nullable fields in your objects, which you eventually expect to contain some values, *null-checks are inevitable.

Code clattered with null-checks can be difficult to read, but it doesn't imply that null-checks are the root of all evil. It's just the consequence of your design decisions.

If you want to reduce the number of null-checks in your code, then assign default values (0 for Integer, BigDecimal.ZERO, empty collections, etc.) instead of keeping null references in every place where it doesn't harm the logic of the application.

Sidenote: contrary to some languages like type TypeScript, in Java there's no notion of optional properties, which was initially mentioned in the title of the question. Practices that are suitable for one language aren't necessarily applicable in another. If you want to leverage Java to its full power, reproducing your experience with frontend languages isn't the right way to go.

Sign up to request clarification or add additional context in comments.

5 Comments

Hi sorry for the late response. I was on holiday. I have now accepted this answer.
Would you say that using Optional to do a null check when fetching data from a database is also an antipattern? For example, see the code in this question (ignore the method signature, just look at the return statement) stackoverflow.com/questions/51690886/…
@Rosa Using Optional to perform null-checks is an antipattern (it doesn't matter where the data comes from) as I said in the answer. Regarding the question you refer to - have a look at the answer to it (there's no misuse optional there).
@Rosa Also have a look at this question: "Should Optional.ofNullable() be used for null check?".
@Rosa You can use methods from the Objects class like requireNonNull() to perform validation. They were designed specifically for that purpose.
0

I think you should define models with default values for additional attributes instead of accept null value. Example, with an Integer attribute, default value is 0 and String attribute is empty string

1 Comment

I thought about that but sometimes a default value doesn't make sense, so you end up using values like 0 or -1 or INFINITY which is even worse because you have to check for those explicitly.
0

I would not discount Optional, OptionalInt and such, even if the persistent database column uses NULL.

Indeed it would be cumbersome:

product.getQuantityAvailable()
    .ifPresent((available) -> {
          product.getQuantityReserved()
              .ifPresent((reserved) -> {
                    if (available < reserved) {
                      System.out.println("Error");
                    }
                  });
        });

But with a map from OptionalInt to IntStream:

if (product.getQuantityAvailable()
        .map().anyMatch(av -> product.getQuantityReserved()
            .map.anyMatch(res -> av < res))) {
    System.out.println("Error");
}

You could store the predicate lambdas separately, say in the product.

The code is more readable, less errorprone, though a bit )))-ish.

1 Comment

I guess having the lambdas elsewhere is a good idea, but I've just tried this and found these kind of condition checks were usually unique so I could not reuse them much. Thanks for the suggestion though I'll play with it.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.