Inside Guice 2: injecting null
Part 2 in a Series.Background: marking nulls
Null is bad. Most of the time, use of
null
indicates clumsy modeling. Instead of null collections, use empty ones. Instead of a null service, use a no-op implementation. The JDK gets this wrong for comparators by using null
as a stand-in for natural order; the result is special cases instead of polymorphism.To help you to detect null problems sooner, Guice refuses to inject null by default. When asked to do so, it will fail with a
ProvisionException
instead. This increases your confidence in your app by eliminating an entire category of runtime problems.But sometimes — and only sometimes — null is necessary. Value objects don't lend themselves to the null object pattern. And in some cases you can be sure that a null reference won't be used. To make intentional null use explicit, you can use one of several
@Nullable
annotations, including those from FindBugs, IntelliJ or old snapshots of Google Collections. These will soon be standardized by another @Nullable
in JSR 305.Injecting null
Guice tolerates
null
to be injected wherever a parameter is annotated @Nullable
: public Person(String firstName, String lastName, @Nullable Phone phone) {
this.firstName = checkNotNull(firstName, "firstName");
this.lastName = checkNotNull(lastName, "lastName");
this.phone = phone;
}
But which
@Nullable
?Rather than arbitrarily choosing an
@Nullable
to support (and alienating everyone else), Guice permits any Nullable
annotation to be used. And I do mean any: it scans the injected parameter or field's annotations, looking at their names. If any has the simple name Nullable
, it is honoured. This means that even your company's internal com.company.util.Nullable
will work.More generally
This trick works particularly nicely for annotations, since they tag objects without adding behaviour. It also avoids an aggravating problem, where code that looks right doesn't work right. Consider:
import com.google.inject.Inject;
import javax.annotation.Named;
public class RealPaymentService implements PaymentService {
@Inject
public RealPaymentService(@Named("creditcard") Processor processor) {
...
}
}
This code appears legitimate, and it might even execute without error. But the wrong
@Named
annotation is applied! The unreleased javax.annotation.Named
is incompatible with Guice and has no effect. Since it looks the same as com.google.inject.name.Named
, there's potential for error.When applying annotations, be careful about overlapping names. When processing them, be mindful of mistakes! What's the annotation-equivalent of a precondition? If a user applies an annotation incorrectly, is it detected?
Part 3.