Reputation: 129
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?
Upvotes: 6
Views: 5583
Reputation: 22595
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.
Upvotes: 1
Reputation: 22595
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())
Upvotes: 1
Reputation:
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());
Upvotes: 2
Reputation: 129
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...
Upvotes: 1
Reputation: 1597
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());
}
Upvotes: 7