How to Avoid NullPointerException in Java Part 2


Don’t be unnecessarily null tolerant

  • Don’t use @Nonnull; non-nullability is implied
  • Assume nonnull unless @Nullable explicitly
  • Check Method Arguments for null early and throw exception (IllegalArgumentException) with meaningful error message
  • Use Preconditions.checkNotNull() or Verify.verifyNotNull() to convert a @Nullable value into a non-null.

Use Non-Null Default Value

  • Use empty collections over null
  • Use empty string over null
  • Use Optional instead of a Nullable Foo
  • Use empty array instead over null Good Example:
@Override
public byte[] NoOpDb.key() {
 return new byte[0];
}

Bad Example:

@Override
public byte[] NoOpDb.key() {
 return null;
}
@Nullable at Method Parameters
  • Add @Nullable to method params help prevent bugs in future change.
// params is non-null, and the elements in params are also non-null.
public void method(String... params) {}

// params may be null, but the elements in params are non-null.
public void method(String @Nullable ... params) {}

// params is non-null, and the elements in params are nullable.
public void method(@Nullable String... params) {}

Use a local variable

If the map is mutable, the value maybe null when refer it later.

if (instanceMap.containsKey(key)) {
  String value = instanceMap.get(key)
}

We can use lock to fix the issue, or in some cases, we can just save the value in a local varible.

String value = instanceMap.get(key);
if (value != null) {
}

@UnderInitialization

If a method has @UnderInitialization, it can be called only from constructor.

@RequiresNonNull("context")  // This method requires context is non-null.
@EnsuresNonNull("resources")  // resources will be non-null.
private void init(@UnderInitialization MyClass this) {
    resources = context.getResources();
}

private void init1(@UnderInitialization(ParentClass.class) MyClass this) {
    // You can access to parent-class's fields here.
}

private void init2(@UnderInitialization(MyClass.class) MyClass this) {
    // You can access to all fields here.
}

@UnknownInitialization

@UnknownInitialization will act like all fields in the class are @Nullable requiring null checks for even non-null fields.

Use @Nollable

MonotonicNonNull

  • Use @MonotonicNonNull if once the field becomes non-null, it never becomes null again/
UnderInitialization

When to use @Nullable

  • Add @Nullable to method return value tells the caller the need to check the nullness.
  • Add @Nullable to method params help prevent bugs in future change.
  • method that never returns null should still be marked @Nullable if its subtypes may return null.
Array

arrays can both be null, and contain null objects:

  • Object[] array is same as @NonNull Object @NonNull [] array: Non-null array of non-null elements.
  • @Nullable Object[] is a non-null array of nullable objects, and Object @Nullable [] is a nullable array of non-null objects.
import org.checkerframework.checker.nullness.compatqual.NullableType;
String @Nullable [] array = null; // Nullable array of non-null elements
array = new String[] {null};  // ERROR!

@Nullable String[] array = new String[] {null};  // Non-null array of nullable elements.
array = null;  // ERROR!

@Nullable String @Nullable [] array = new String[] {null};  // Nullable array of nullable elements.
array = null;  // OK.

Array<@NullableType String> array; // Non-null array with nullable elements.
String @NullableType[] array // Nullable array of non-null objects.
toArray()
  • Use list.toArray(new TheType[list.size()]) instead of list.toArray(new TheType[list.size()]), as nullness checker can’t infer that the size of the array you created is the same size as the collection passed in.

Generics

@Nullable List implies the list can be null, and List<@Nullable String> implies the list can contain nulls. ListenableFuture<@Nullable Foo> implies the the result in the future can be null.

Useful Checker Annotations for Nullness Analysis

  • @Nullable
  • @PolyNull: A method returns null only if a parameter is null
  • @MonotonicNonNull: it would be non-null once it is initialized to non-null.
  • EnsuresNonNullIf: ensures that some fields/methods will be/return non-null if the return value satisfy a condition.
@EnsuresNonNullIf(expression = "list", result = true)
public boolean hasNext() {
    return list != null && index < list.length;
}
@EnsuresNonNullIf(expression="#1", result=true)
  • @SideEffectFree: the method doesn’t have external-visible side effects.
  • @Deterministic: the method returns the identical result for the the same params.
  • @Pure: a combination of @SideEffectFree and @Deterministic.

@SuppressWarnings

  • Only @SuppressWarnings(“nullness”) in exceptional cases.
  • Use specific suppression at the narrowest scope possible
    • @SuppressWarnings("nullness:argument.type.incompatible") instead of the broad scope @SuppressWarnings("nullness")

Labels

adsense (5) Algorithm (69) Algorithm Series (35) Android (7) ANT (6) bat (8) Big Data (7) Blogger (14) Bugs (6) Cache (5) Chrome (19) Code Example (29) Code Quality (7) Coding Skills (5) Database (7) Debug (16) Design (5) Dev Tips (63) Eclipse (32) Git (5) Google (33) Guava (7) How to (9) Http Client (8) IDE (7) Interview (88) J2EE (13) J2SE (49) Java (186) JavaScript (27) JSON (7) Learning code (9) Lesson Learned (6) Linux (26) Lucene-Solr (112) Mac (10) Maven (8) Network (9) Nutch2 (18) Performance (9) PowerShell (11) Problem Solving (11) Programmer Skills (6) regex (5) Scala (6) Security (9) Soft Skills (38) Spring (22) System Design (11) Testing (7) Text Mining (14) Tips (17) Tools (24) Troubleshooting (29) UIMA (9) Web Development (19) Windows (21) xml (5)