JavaAnnotations

This tutorial describes how to use null annotations to avoid null pointer exceptions and how to define your own annotations and how to use Java reflection to analyze your code with it.

1. Defining the API contract via null annotations

1.1. Eclipse IDE and null annotations

Annotations can be used to provide additional meta information about your API. Eclipse JDT provides the following annotations which can be used:

  • @NonNull: null is not a legal value

  • @Nullable: null value is allowed and must be expected

These annotations can be placed on:

  • Method parameter

  • Method return (syntactically a method annotation is used here)

  • Local variables

  • Fields

You can also specify the default value via the @NonNullByDefault annotation which can be specified for a method, a type or a package (via a file package-info.java). To specify it for a whole package, create the following package-info.java file in your package, for example, com.vogella.test.nullannatations.

@NonNullByDefault
package com.vogella.test.nullannatations;
import org.eclipse.jdt.annotation.NonNullByDefault;

1.2. Adding support for null annotations

1.2.1. Maven

<dependency>
    <groupId>org.eclipse.jdt</groupId>
    <artifactId>org.eclipse.jdt.annotation</artifactId>
    <version>2.0.0</version>
</dependency>

1.2.2. Gradle

compile 'org.eclipse.jdt:org.eclipse.jdt.annotation:2.0.0'

1.2.3. OSGi

Add an optional dependency to org.eclipse.jdt.annotation in your MANIFEST.MF

1.2.4. Managing the classpath manually

Eclipse provides a quickfix for adding the required dependency. Simple use @NonNull in your code and use Ctrl+1 to add the required dependency.

1.3. Activating annotation based null pointer access analysis

Select Windows  Java  Compiler  Errors/Warnings and select Enable annotation-based null analysis.

annotationbasednullanalysis10

2. Defining custom annotations

2.1. Define your custom annotation

The Java programming language allows you to define your custom annotations. Annotations are defined via the @interface annotation before the class name. Via @Retention you define if the annotation should be retained at runtime or not. The @Target annotation lets you define where this annotation can be used, e.g., the class, fields, methods, etc.

A typical annotation definition would look like the following.

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface InstallerMethod {
}

You can add additional information, for example you can define that your annotation is a qualifier for the `@Inject`annotation.

@javax.inject.Qualifier
@Documented
@Target({ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
public @interface Checker {

}

2.2. Accessing your annotation via Java reflection

To process your annotation you could write your own annotation processor. Typically you use Java reflection for this. Java reflection allows you to analyze a Java class and use the information contained in this class at runtime.

3. Exercise: Define custom annotation and access via reflection

Create a new Java project called com.vogella.annotations. Create the following two classes. The first class defines an annotation and the second class uses this to mark certain methods.

package com.vogella.annotations;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(value = ElementType.METHOD)
@Retention(value = RetentionPolicy.RUNTIME)
public @interface CanRun {

}
package com.vogella.annotations;

import java.lang.reflect.Method;

public class AnnotationRunner {

    public void method1() {
        System.out.println("method1");
    }

    @CanRun
    public void method2() {
        System.out.println("method2");
    }

    public void method3() {
        System.out.println("method3");
    }

    @CanRun
    public void method5() {
        System.out.println("method4");
    }

}

Afterwards create the following test class. The main method of this class analyzes the annotations and calls the corresponding methods.

package com.vogella.annotations;

import java.lang.reflect.Method;

public class MyTest {

    public static void main(String[] args) {

        AnnotationRunner runner = new AnnotationRunner();
        Method[] methods = runner.getClass().getMethods();

        for (Method method : methods) {
            CanRun annos = method.getAnnotation(CanRun.class);
            if (annos != null) {
                try {
                    method.invoke(runner);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

4. JavaAnnotations resources