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.

4 comments:

yogurtearl said...

Typo: I assume you meant RealPaymentService to be a constructor, since it doesn't have a return type, but it doesn't match the name of the class CreditCardPaymentService.

Also, does Guice 2 work with AppEngine4Java out of the box?

swankjesse said...

Whoops! Typo fixed.

Guice works with App Engine out-of-the-box. Setup instructions are here:
http://code.google.com/p/google-guice/wiki/GoogleAppEngine

Faber said...

And naturally both the class and the constructor will be package private in practice =)

Dan Lewis said...

part 3 link is broken