Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Info
iconfalse

The content of this page is the result of dicussions on Expected Errors.

These guidelines have been approved after being reviewed on the Pending error handling guidelines page.

 

Best practices

Return empty arrays or collections, not nulls.

(c) Joshua Bloch, Effective Java

TODO: investigate what can be done in "Sonar for Sonar" profile to enforce this rule.

Every parameter is non-null unless explicitly specified.

Package which follows this rule should contain package-info.java :

Code Block
languagejava
@ParametersAreNonnullByDefault
package packageName;
 
import javax.annotation.ParametersAreNonnullByDefault;

Form of nullability declaration :

Code Block
languagejava
import javax.annotation.Nullable;
Type methodName(@Nullable Type parameter) {
  ...
}

Note that main point in this statement is not an annotation, but a principle.

Expand
titleFindbugs Rules
  • NP_NULL_PARAM_DEREF_ALL_TARGETS_DANGEROUS ( Correctness - Method call passes null for nonnull parameter (ALL_TARGETS_DANGEROUS) )
  • NP_NONNULL_PARAM_VIOLATION ( Correctness - Method call passes null to a nonnull parameter )
  • NP_PARAMETER_MUST_BE_NONNULL_BUT_MARKED_AS_NULLABLE ( Dodgy - Parameter must be nonnull but is marked as nullable )
  • NP_STORE_INTO_NONNULL_FIELD ( Correctness - Store of null value into field annotated NonNull )
  • NP_NULL_ON_SOME_PATH_FROM_RETURN_VALUE ( Dodgy - Possible null pointer dereference due to return value of called method )
  • NP_NONNULL_RETURN_VIOLATION ( Correctness - Method may return null, but is declared @NonNull )
Expand
titleReferences
Throw early and use Preconditions.

Preconditions on parameters are required on API to establish contract between API and consumer of API. Then it's not mandatory for backend to verify again that parameters are correct.

Code Block
languagejava
void parse(String filename) {
  Preconditions.checkNotNull(filename, "filename can't be null"); // otherwise stack-strace will contain line inside constructor of FileInputStream, when we will execute it
  ... // maybe huge amount of code
  InputStream is = new FileInputStream(filename);
  ...
}

Also this means that NullPointerException, IllegalArgumentException and IllegalStateException are technical exceptions and should not be shown in their original form to the end-user : compare "NullPointerException: filename can't be null" and "Unable to execute sensor Surefire: NullPointerException". Thus even short form of precondition without message is efficient :

Code Block
languagejava
Preconditions.checkNotNull(parameter);
Preconditions.checkArgument(expression);
Preconditions.checkState(expression);
Every class is not thread safe unless explicitly specified.

Form of declaration :

Code Block
languagejava
import javax.annotation.concurrent.ThreadSafe;
 
@ThreadSafe
class ClassName {
  ...
}

Note that immutable classes are thread safe by definition and so this declaration is redundant for them.

Implementations of custom Exceptions must be immutable.
Use ExpectedException rule from JUnit for assertions on Exception.

This JUnit rule replaces the property "expected" of the annotation @Rule.

Code Block
languagejava
@Rule
public ExpectedException thrown = ExpectedException.none();
 
@Test
public void test() {
  thrown.expect(SonarException.class);
  thrown.expectMessage("message");
  someMethod(); // throws SonarException
}
Annotate methods which visibility is relaxed to make the code testable

Use the Guava annotation @VisibleForTesting :

Code Block
languagejava
@com.google.common.annotations.VisibleForTesting
String packageVisibleBecauseOfUnitTest() {
}

Anti patterns

Log and Throw / Rethrow.
Exceptions as flow control.
Expand
titlePMD Rule

 ExceptionAsFlowControl

Declaring exceptions other than Exception in signatures of test methods.
Catch and Ignore.

(c) Joshua Bloch, Effective Java, Item 65

When a method from some API throws a checked exception, it's trying to tell you that you should take some counter action. Do not ignore this by catching in empty block and continue as if nothing happened !

First of all - if you can recover, then recover !

Second - if checked exception does not make sense to you, then do not hesitate to convert it into an unchecked exception and throw it again : 

Code Block
titleGood
languagejava
try {
  someMethod();
} catch (CheckedException e) {
  throw SomeRuntimeException(...);
}

As a last resort - use logging :

Code Block
titleGood enough
languagejava
try {
  someMethod();
} catch (CheckedException e) {
  LOG.warn(...);
}
Expand
titleFindbugs Rules
  • DE_MIGHT_IGNORE
  • DE_MIGHT_DROP
Destructive wrapping.

Destructive wrapping means that original exception will be lost together with all information like stack trace. Forms of destructive wrapping :

Code Block
titleBad
languagejava
try {
  ...
} catch (SomeException e) {
  throw new AnotherException();
  throw new AnotherException(message);
  throw new AnotherException(e.getMessage());
}

Original exception should be always added into wrapped exception. If such constructor does not exist, then use method initCause, which is always available, because declared in Throwable, e.g. :

Code Block
titleGood
languagejava
try {
  ...
} catch (SomeException e) {
  javax.persistence.NonUniqueResultException e = new NonUniqueResultException();
  e.initCause(e);
  throw e;
}
Expand
titlePMD Rule

AvoidLosingExceptionInformation

Throw from Within Finally.

Following code is fine as long as cleanUp() can never throw an exception :

Code Block
try {
  someMethod();
} finally {
  cleanUp();
}

If someMethod() throws an exception, and then cleanUp() throws an exception, then second exception will swallow first one. If cleanUp() can possibly throw an exception, then make sure that you either handle it, or log it.

Expand
titlePMD Rule

DoNotThrowExceptionInFinally

Return from Finally.

This can discard exceptions.

Expand
titlePMD Rule

ReturnFromFinallyBlock

Returning null when operation is unsupported.

Instead - throw an exception.