Check not null in Java
Asked Answered
I

5

6

Imagine I have, say, XML-generated entity in Java that holds some data I need. For example:

<Car>
   <Engine>
      <Power>
         175
      </Power>
   </Engine>
</Car>

So if I need an engine power, I, followed by the best practices of business software development, will do the next thing:

Car car = dao.getCar()
Power power = car != null && car.engine != null ? power : null
return power

I hate this. Sometimes it seems that half of the code is just null checks.

Any ideas?

Insinuation answered 8/9, 2017 at 11:32 Comment(7)
That's the design of your XML...Covenanter
I'm going to flag this as "unclear what you are asking."Vicarious
potentially, any of these nodes is not present. You can check your xml file against a xsd schema, otherwise you have to do the checks manually.Dangerfield
@HeikkiMäenpää why is this unclear? The question is about how to eliminate boilerplate code.Dangerfield
Is that valid for a car to not have an engine? Is that valid for an engine to not have a power? If not, then you should not do these null-checks, because the values should never be null, and if they are, you'd better fail early to make the bug obviously apparent, rather than returning null and clutter your code.Hamiltonian
Agree with JB Nizet. It is because my example is bad. Let's imagine the Engine can be null from business point of view.Insinuation
@Dangerfield The way it was titled and phrased, it seemed to me to be a rant about something OP may or may not have designed himself.Vicarious
K
7

Take a look at Java 8 Optional class. It does exactly that: it avoids the ugly checks on null.

In your case, you could use this snippet of code to avoid them:

Car car = dao.getCar();
Optional<Car> optionalCar = Optional.ofNullable(car); 
Optional<Power> optionalPower = getPowerIfPresent(optionalCar);

Power power = Optional.empty();
if(optionalPower.isPresent()) {
    power = optionalPower.get();
}

after writing a function that returns the power of a given car:

public static Optional<Power> getPowerIfPresent(Optional<Car> car) {
    return car
        .flatMap(c -> c.getEngine())
        .map(e -> e.getPower());
}
Kachine answered 8/9, 2017 at 11:43 Comment(2)
Interesting approach, thanks! But it does not seem to be generic enough. I mean, I still need to create a lot of methods: getPowerIfPresent, getColorIfPresent, getEngineLeftPanelNumberOfBoltsIfPresent ...Insinuation
No problem, it's just that your question didn't state this very explicitly in the first place or I might have over-simplified it.Kachine
D
2

This is the same as using of Optional, but might be more readable:

public class NullSafe<T> {
    private final T value;
    public NullSafe(@Nullable T value) { this.value = value; }
    public static <T> NullSafe<T> of(@Nullable T value) { return new NullSafe<>(value); }

    public <R> NullSafe<R> get(Function<T,R> mapper) {
        R newValue = (value != null) ? mapper.apply(value) : null;
        return new NullSafe<>(newValue);
    }

    public T nullable() { return value; }
    public T orDefault(T defaultValue) { return (value != null) ? value : defaultValue; }
}

And usage:

Power power = NullSafe.of(dao.getCar())
    .get(Car::getEngine)
    .get(Engine::getPower)
    .nullable(); // .orDefault(Power.defaultPower());

An alternative can be static methods:

public static <R> R get(Supplier<R> supplier, R defaultValue) {
    try {  return supplier.get(); } 
    catch (NullPointerException ex) { return defaultValue; }
}

public static <R> R getNullable(Supplier<R> supplier) { return get(supplier, null); }

And usage:

Power power = NullSafe.get(() -> dao.getCar().getEngine().getPower(), Power.defaultPower());
Power powerOrNull = NullSafe.getNullable(() -> dao.getCar().getEngine().getPower());
Dwain answered 8/9, 2017 at 11:59 Comment(1)
Thank you! I've build the same thing in my project, but without any generic, unfortunately. As I have Java 6 restriction.Insinuation
I
1

Or maybe create some util method:

static <T> Optional<T> tryGet(Supplier<T> getter) {
    try {
        return Optional.ofNullable(getter.get());
    } catch(NullPointerException ignored) {
        return Optional.empty();
    }
}

Then you could use it like this:

System.out.println(tryGet(() -> car.engine.power).orElse(new Power()));

There is a library no-exception that does that, but you cannot specify it to only "silence" NPEs.

Exceptions.silence().get(() -> car.engine.power).orElse(new Power())
Inulin answered 8/9, 2017 at 11:51 Comment(1)
Great! Thank you, Sir! This is exactly the answer I've been trying to find!Insinuation
I
1

My own approach kind of this now:

public class CarDataExtractor {

   private final Car car;

   private CarDataExtractor(Car car) {
       this.car = car;
   }

  public static CarDataExtractor on(Car car) {
       return new CarDataExtractor(car);
   }

   public EngineDataExtractor engine() {
       return car != null && car.getEngine() != null
               ? EngineDataExtractor.on(car.getEngine())
               : EngineDataExtractor.on(null);
   }

   public Car self() {
       return car;
   }
}

public class EngineDataExtractor {

   private final Engine engine;

   private EngineDataExtractor(Engine engine) {
       this.engine = engine;
   }

   public static EngineDataExtractor on(Engine engine) {
       return new EngineDataExtractor(engine);
   }

   public PowerDataExtractor engine() {
       return engine != null && engine.getPower() != null
               ? PowerDataExtractor.on(engine.getPower())
               : PowerDataExtractor.on(null);
   }

   public Engine self() {
       return engine;
   }
}

...

Power power = CarDataExtractor.on(dao.getCar()).engine().power().self()

It is because I am restricted to Java 6...

Insinuation answered 8/9, 2017 at 12:21 Comment(2)
I think you're over-engineering this. Just write simple method 'extractPower', which would encapsulate null-checks and this should be enough.Gev
It is not just about the engine power. In my project there is an a great entity built from XML. And there are hundreds of duplicates with null-checks around the codeInsinuation
I
1

There is also another option, you could use, which might be helpful for you if you're using Spring.

If you're not using Spring you would need to add additional dependency to your project.

Using Spel you could do:

ExpressionParser parser = new SpelExpressionParser();

StandardEvaluationContext context = new StandardEvaluationContext(dao.getCar());

Power power = parser.parseExpression("engine?.power").getValue(context, Power.class);

In expression engine?.power safe navigation operator is being used. In case engine is null, then the whole expression will evaluate to null.

This solution will work on Java 6.

Inulin answered 8/9, 2017 at 12:36 Comment(2)
Thank you! I've never heard about Spell before. But I afraid it can add a significant overhead to the performance, through for some use cases I think its usage is pretty helpfulInsinuation
Please have a look at this section: SpEL compilationGev

© 2022 - 2025 — McMap. All rights reserved.