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?

  • That's the design of your XML... – Murat Karagöz Sep 08 '17 at 11:33
  • I'm going to flag this as "unclear what you are asking." – Haem Sep 08 '17 at 11:36
  • 4
    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. – f1sh Sep 08 '17 at 11:36
  • 1
    @HeikkiMäenpää why is this unclear? The question is about how to eliminate boilerplate code. – f1sh Sep 08 '17 at 11:37
  • 2
    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. – JB Nizet Sep 08 '17 at 11:45
  • Agree with JB Nizet. It is because my example is bad. Let's imagine the Engine can be null from business point of view. – Andrei Zhaleznichenka Sep 08 '17 at 11:49
  • @f1sh 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. – Haem Sep 08 '17 at 13:07

5 Answers5

8

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());
}
aUserHimself
  • 1,589
  • 2
  • 17
  • 26
  • 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 ... – Andrei Zhaleznichenka Sep 08 '17 at 11:57
  • 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. – aUserHimself Sep 08 '17 at 11:58
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());
matoni
  • 2,479
  • 21
  • 39
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())
Krzysztof Atłasik
  • 21,985
  • 6
  • 54
  • 76
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...

  • 1
    I think you're over-engineering this. Just write simple method 'extractPower', which would encapsulate null-checks and this should be enough. – Krzysztof Atłasik Sep 08 '17 at 12:39
  • 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 code – Andrei Zhaleznichenka Sep 08 '17 at 12:47
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.

Krzysztof Atłasik
  • 21,985
  • 6
  • 54
  • 76
  • 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 helpful – Andrei Zhaleznichenka Sep 08 '17 at 12:45
  • 1
    Please have a look at this section: [SpEL compilation](https://docs.spring.io/spring/docs/current/spring-framework-reference/html/expressions.html#expressions-spel-compilation) – Krzysztof Atłasik Sep 08 '17 at 12:51