Getting Annotation values at run-time and Annotations for this and receiver parameters


Getting Annotation values at run-time

You can fetch the current properties of the Annotation by using Reflection to fetch the Method or Field or Class which has an Annotation applied to it, and then fetching the desired properties.

@Retention(RetentionPolicy.RUNTIME)
@interface MyCustomAnnotation {
    String attribute1() default "defaultAttribute1";
    String attribute2() default "defaultAttribute2";
}
 
class CustomAnnotationExample {
 
    @MyCustomAnnotation
    public void demonstrateDefaults() throws Exception {
        Method method = CustomAnnotationExample.class.getMethod("demonstrateDefaults", null);
        MyCustomAnnotation annotation = (MyCustomAnnotation) method.getAnnotation(MyCustomAnnotation.class);
        print(annotation);
    }
 
    @MyCustomAnnotation(attribute1 = "customValue1", attribute2 = "customValue2")
    public void demonstrateCustomValues() throws Exception {
        Method method = CustomAnnotationExample.class.getMethod("demonstrateCustomValues", null);
        MyCustomAnnotation annotation = (MyCustomAnnotation) method.getAnnotation(MyCustomAnnotation.class);
        print(annotation);
    }
 
    public void print(MyCustomAnnotation annotation) {
        System.out.println("Attribute1 = " + annotation.attribute1() + ", Attribute2 = " + annotation.attribute2());
    }
 
    public static void main(String[] args) {
        CustomAnnotationExample example = new CustomAnnotationExample();
        try {
            example.demonstrateDefaults();
            example.demonstrateCustomValues();
        } catch (Exception e) {
            System.err.println("Exception [" + e.getClass().getName() + "] - " + e.getMessage());
            e.printStackTrace(System.err);
        }
    }
}

Output

Attribute1 = defaultAttribute1, Attribute2 = defaultAttribute2
Attribute1 = customValue1, Attribute2 = customValue2

Annotations for 'this' and receiver parameters

When Java annotations were first introduced there was no provision for annotating the target of an instance method or the hidden constructor parameter for an inner classes constructor.

The following example illustrates the syntax for both kinds of receiver parameter:

public class Outer {
	public class Inner {
		public Inner (Outer this) {
			// ...
		}
		public void doIt(Inner this) {
			// ...
		}
	}
}

The sole purpose of receiver parameters is to allow you to add annotations. For example, you might have a custom annotation @IsOpen whose purpose is to assert that a Closeable object has not been closed when a method is called. For example:

public class MyResource extends Closeable {
	public void update(@IsOpen MyResource this, int value) {
		// ...
	}
 
	public void close() {
		// ...
	}
}

At one level, the @IsOpen annotation on this could simply serve as documentation. However, we could potentially do more. For example:

  • An annotation processor could insert a runtime check that this is not in closed state when update is called.
  • A code checker could perform a static code analysis to find cases where this could be closed when update is called.

Add multiple annotation values

An Annotation parameter can accept multiple values if it is defined as an array. For example the standard annotation @SuppressWarnings is defined like this:

public @interface SuppressWarnings {
   String[] value();
}

The value parameter is an array of Strings. You can set multiple values by using a notation similar to Array initializers:

@SuppressWarnings({"unused"})
@SuppressWarnings({"unused", "javadoc"})

If you only need to set a single value, the brackets can be omitted:

@SuppressWarnings("unused")

Basic Programs