Chromium Java style guide

For other languages, please see the Chromium style guides.

Chromium follows the Android Open Source style guide unless an exception is listed below.

You can propose changes to this style guide by sending an email to java@chromium.org. Ideally, the list will arrive at some consensus and you can request review for a change to this file. If there's no consensus, //styleguide/java/OWNERS get to decide.

Java 8 Language Features

Desugar is used to rewrite some Java 7 & 8 language constructs in a way that is compatible with Java 6 (and thus all Android versions). Use of these features is encouraged, but there are some gotchas:

Default Interface Methods

  • Desugar makes default interface methods work by copy & pasting the default implementations into all implementing classes.
  • This technique is fine for infrequently-used interfaces, but should be avoided (e.g. via a base class) if it noticeably increases method count.

Lambdas and Method References

  • These are syntactic sugar for creating anonymous inner classes.
    • Furthermore, stateless lambdas become singletons and so do not result in new instances when used in loops.
  • Use them only where the cost of an extra class & method definition is justified.

try-with-resources

  • Some library classes do not implement Closeable on older platform APIs. Runtime exceptions are thrown if you use them with a try-with-resources. Do not use the following classes in a try-with-resources:
    • java.util.zip.ZipFile (implemented in API 19)
    • java.net.Socket (implemented in API 19)

Other Language Features & APIs

Exceptions

  • As with the Android style guide, we discourage overly broad catches via Exception / Throwable / RuntimeException.
    • If you need to have a broad catch expression, use a comment to explain why.
  • Catching multiple exceptions in one line is fine.

It is OK to do:

try {
  somethingThatThrowsIOException(filePath);
  somethingThatThrowsParseException(filePath);
} catch (IOException | ParseException e) {
  Log.w(TAG, "Failed to read: %s", filePath, e);
}
  • Avoid adding messages to exceptions that do not aid in debugging.

For example:

try {
  somethingThatThrowsIOException();
} catch (IOException e) {
  // Bad - message does not tell you more than the stack trace does:
  throw new RuntimeException("Failed to parse a file.", e);
  // Good - conveys that this block failed along with the "caused by" exception.
  throw new RuntimeException(e);
  // Good - adds useful information.
  throw new RuntimeException(String.format("Failed to parse %s", fileName), e);
}

Logging

  • Use org.chromium.base.Log instead of android.util.Log.
    • It provides %s support, and ensures log stripping works correctly.
  • Minimize the use of Log.w() and Log.e().
    • Debug and Info log levels are stripped by ProGuard in release builds, and so have no performance impact for shipping builds. However, Warning and Error log levels are not stripped.
  • Function calls in log parameters are not stripped by ProGuard.
Log.d(TAG, "There are %d cats", countCats());  // countCats() not stripped.

Asserts

The Chromium build system strips asserts in release builds (via ProGuard) and enables them in debug builds (or when dcheck_always_on=true) (via a build step). You should use asserts in the same scenarios where C++ DCHECK()s make sense. For multi-statement asserts, use org.chromium.base.BuildConfig.DCHECK_IS_ON to guard your code.

Example assert:

assert someCallWithoutSideEffects() : "assert description";

Example use of DCHECK_IS_ON:

if (org.chromium.base.BuildConfig.DCHECK_IS_ON) {
  // Any code here will be stripped in Release by ProGuard.
  ...
}

Finalizers

In line with Google's Java style guide, never override Object.finalize().

Custom finalizers:

  • are called on a background thread, and at an unpredicatble point in time,
  • swallow all exceptions (asserts won't work),
  • causes additional garbage collector jank.

Classes that need destructor logic should provide an explicit destroy() method.

Other Android Support Library Annotations

  • Use them! They are documented here.
    • They generally improve readability.
    • Some make lint more useful.
  • javax.annotation.Nullable vs android.support.annotation.Nullable
    • Always prefer android.support.annotation.Nullable.
    • It uses @Retention(SOURCE) rather than @Retention(RUNTIME).

Tools

Automatically formatting edited files

A checkout should give you clang-format to automatically format Java code. It is suggested that Clang's formatting of code should be accepted in code reviews.

You can run git cl format to apply the automatic formatting.

IDE Setup

For automatically using the correct style, follow the guide to set up your favorite IDE:

Checkstyle

Checkstyle is automatically run by the build bots, and to ensure you do not have any surprises, you can also set up checkstyle locally using this guide.

Lint

Lint is run as part of the build. For more information, see here.

Style / Formatting

File Headers

TODOs

  • TODO should follow chromium convention. Examples:
    • TODO(username): Some sentence here.
    • TODO(crbug.com/123456): Even better to use a bug for context.

Code formatting

  • Fields should not be explicitly initialized to default values (see here).

Curly braces

Conditional braces should be used, but are optional if the conditional and the statement can be on a single line.

Do:

if (someConditional) return false;
for (int i = 0; i < 10; ++i) callThing(i);

or

if (someConditional) {
  return false;
}

Do NOT do:

if (someConditional)
  return false;

Import Order

  • Static imports go before other imports.
  • Each import group must be separated by an empty line.

This is the order of the import groups:

  1. android
  2. com (except com.google.android.apps.chrome)
  3. dalvik
  4. junit
  5. org
  6. com.google.android.apps.chrome
  7. org.chromium
  8. java
  9. javax

This is enforced by the Chromium Checkstyle configuration under the ImportOrder module.

Location

“Top level directories” are defined as directories with a GN file, such as //base and //content, Chromium Java should live in a directory named <top level directory>/android/java, with a package name org.chromium.<top level directory>. Each top level directory's Java should build into a distinct JAR that honors the abstraction specified in a native checkdeps (e.g. org.chromium.base does not import org.chromium.content). The full path of any java file should contain the complete package name.

For example, top level directory //base might contain a file named base/android/java/org/chromium/base/Class.java. This would get compiled into a chromium_base.jar (final JAR name TBD).

org.chromium.chrome.browser.foo.Class would live in chrome/android/java/org/chromium/chrome/browser/foo/Class.java.

New <top level directory>/android directories should have an OWNERS file much like //base/android/OWNERS.

Miscellany

  • Use UTF-8 file encodings and LF line endings.