The Checker Framework Manual:
Custom pluggable types for Java

Chapter 1 Introduction

The Checker Framework enhances Java’s type system to make it more powerful and useful. This lets software developers detect and prevent errors in their Java programs.

A “checker” is a compile-time tool that warns you about certain errors or gives you a guarantee that those errors do not occur. The Checker Framework comes with checkers for specific types of errors:

These checkers are easy to use and are invoked as arguments to javac.

The Checker Framework also enables you to write new checkers of your own; see Chapters 30 and 37.

1.1 How to read this manual

If you wish to get started using some particular type system from the list above, then the most effective way to read this manual is:

  • • Read all of the introductory material (Chapters 1–2).

  • • Read just one of the descriptions of a particular type system and its checker (Chapters 3–31).

  • • Skim the advanced material that will enable you to make more effective use of a type system (Chapters 32–41), so that you will know what is available and can find it later. Skip Chapter 37 on creating a new checker.

1.2 How it works: Pluggable types

Java’s built-in type-checker finds and prevents many errors — but it doesn’t find and prevent enough errors. The Checker Framework lets you define new type systems and run them as a plug-in to the javac compiler. Your code stays completely backward-compatible: your code compiles with any Java compiler, it runs on any JVM, and your coworkers don’t have to use the enhanced type system if they don’t want to. You can check part of your program, or the whole thing. Type inference tools exist to help you annotate your code; see Chapter 35.

Most programmers will use type systems created by other people, such as those listed at the start of the introduction (Chapter 1). Some people, called “type system designers”, create new type systems (Chapter 37). The Checker Framework is useful both to programmers who wish to write error-free code, and to type system designers who wish to evaluate and deploy their type systems.

This document uses the terms “checker” and “type-checking compiler plugin” as synonyms.

1.3 Installation

This section describes how to install the Checker Framework.

  • • If you use a build system that automatically downloads dependencies, such as Gradle or Maven, no installation is necessary; just see Chapter 39.

  • • If you wish to try the Checker Framework without installing it, use the Checker Framework Live Demo webpage.

  • • This section describes how to install the Checker Framework from its distribution. The Checker Framework release contains everything that you need, both to run checkers and to write your own checkers.

  • • Alternately, you can build the latest development version from source (Section 41.3).

Requirement: You must have a JDK (version 17 or later) installed.

The installation process has two required steps and one optional step.

  • 1. Download the Checker Framework distribution: https://checkerframework.org/checker-framework-4.3.0.zip

    For example, on Unix you can run: wget https://checkerframework.org/checker-framework-4.3.0.zip

  • 2. Unzip it to create a checker-framework-4.3.0 directory.

    For example, on Unix you can run: unzip checker-framework-4.3.0.zip

  • 3. Configure your IDE, build system, or command shell to include the Checker Framework on the classpath. Choose the appropriate section of Chapter 39.

Now you are ready to start using the checkers.

We recommend that you work through the Checker Framework tutorial, which demonstrates the Nullness, Regex, and Tainting Checkers.

Section 1.4 walks you through a simple example. More detailed instructions for using a checker appear in Chapter 2.

The Checker Framework is released on a monthly schedule. The minor version (the middle number in the version number) is incremented if there are any incompatibilities with the previous version, including in user-visible behavior or in methods that a checker implementation might call.

1.4 Example use: detecting a null pointer bug

This section gives a very simple example of running the Checker Framework. There is also a tutorial that you can work along with.

Let’s consider this very simple Java class. The local variable ref’s type is annotated as @NonNull, indicating that ref must be a reference to a non-null object. Save the file as GetStarted.java.

import org.checkerframework.checker.nullness.qual.*;

public class GetStarted {
    void sample() {
        @NonNull Object ref = new Object();
    }
}

If you run the Nullness Checker (Chapter 3), the compilation completes without any errors.

Now, introduce an error. Modify ref’s assignment to:

  @NonNull Object ref = null;

If you run the Nullness Checker again, it emits the following error:

GetStarted.java:5: incompatible types.
found   : @Nullable <nulltype>
required: @NonNull Object
        @NonNull Object ref = null;
                              ^
1 error

This is a trivially simple example. Even an unsound bug-finding tool like SpotBugs or Error Prone could have detected this bug. The Checker Framework’s analysis is more powerful than those tools and detects more code defects than they do.

Type qualifiers such as @NonNull are permitted anywhere that you can write a type, including generics and casts; see Section 2.1. Here are some examples:

 @Interned String intern() { ... }             // return value
 int compareTo(@NonNull String other) { ... } // parameter
 @NonNull List<@Interned String> messages;     // non-null list of interned Strings