ONJava.com -- The Independent Source for Enterprise Java
oreilly.comSafari Books Online.Conferences.

advertisement

AddThis Social Bookmark Button

Declarative Programming in Java
Pages: 1, 2, 3

Meta-Annotations

Java defines several standard meta-annotations (annotation types designed for annotating annotation type declarations).



The new package java.lang.annotation contains the following meta-annotations:

Meta-annotation Purpose
@Documented Indicates that annotations with a type are to be documented by javadoc and similar tools by default.
@Inherited Indicates that an annotation type is automatically inherited.
@Retention Indicates how long annotations with the annotated type are to be retained. Example: @Retention(RetentionPolicy.RUNTIME) The enumeration RetentionPolicy defines the constants to be used for specifying Retention.
@Target Indicates the kinds of program element to which an annotation type is applicable. Example: @Target({ElementType.FIELD, ElementType.METHOD}) The enumeration ElementType defines constants that are used with the Target meta-annotation type to specify where it is legal to use an annotation type.

Standard Annotations

There are two standard annotations in the java.lang package.

Annotation Purpose
@Deprecated Reincarnation of deprecated javadoc tag as an annotation in JDK 1.5. Compilers warn when a deprecated program element is used or overridden in non-deprecated code.
@Overrides Indicates that a method declaration is intended to override a method declaration in a super-class. If a method is annotated with this annotation type but does not override a super-class method, compilers are required to generate an error message.

The following example illustrates the use of standard annotations.

public class Parent {
    @Deprecated
    public void foo(int x) {
        System.out.println("Parent.foo(int x) called.");
    }
}

public class Child extends Parent {
    @Overrides
    public void foo() {
        System.out.println("Child.foo() called.");
    }
}

My intention is to extend the Parent class and override the foo(int x) method in Child class. By mistake, the foo method in the child does not override the one in the parent, because of the mismatch in the signature. Obviously, this is a bug. In the past, this kind of bug could be identified only at runtime after hours of debugging. Now, the use of the @Overrides annotation can save hours wasted in debugging. When a method is annotated with @Overrides, the compiler will check whether the method in a child class really overrides a method in the parent. The compiler will report an error when no method is overridden.

javac -source 1.5 Parent.java Child.java
Child.java:3: method does not override
a method from its superclass
@Overrides
 ^
1 error

Let's correct the error by modifying the foo method signature, and compile the code again.

public class Child extends Parent{
    @Overrides
    public void foo(int x) {
        System.out.println("Child.foo(int x) called.");
    }
}

javac -Xlint:deprecation -source 1.5 Child.java
Child.java:4: warning:
[deprecation] foo(int) in Parent has been
deprecated
public void foo(int x) {
            ^
1 warning

The foo method is marked as deprecated using the @Deprecated annotation, so the compiler reports a warning, as shown above.

A Complex Example

To keep the introduction simple, I used a single valued annotation. Now it's time to look at a complex one.

public @interface Trademark {
	String description();
	String owner();
}

In the code snippet above we have declared an annotation called Trademark with two members: description and owner, both of which return String.

import java.lang.annotation.*;
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.PACKAGE})
public @interface License {
	String name();
	String notice();
	boolean redistributable();
	Trademark[] trademarks();
}

In the code snippet above, we have declared an annotation called License, which has members that return String, boolean, and an array of Trademark annotations. Now we can use the License annotation.

@License(
name = "Apache",
notice = "license notice ........",
redistributable = true,
trademarks =
   {@Trademark(description="abcd",owner="xyz"),
    @Trademark(description="efgh",owner="klmn")}
)
public class Example2 {
    public static void main(String []args) {
    License lic;
    lic=Example2.class.getAnnotation(License.class);
    System.out.println(lic.name());
    System.out.println(lic.notice());
    System.out.println(lic.redistributable());
    Trademark [] tms = lic.trademarks();
    for(Trademark tm : tms) {
        System.out.println(tm.description());
        System.out.println(tm.owner());
        }
    }
}

The above example shows how to use annotations that have multiple members with String, boolean, and array return types. It also illustrates how to define annotations with parameter values that are annotations. The main method in Example2 illustrates how to access the annotations at runtime.

Defaults and Order of name=value Pairs

import java.lang.annotation.*;

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
public @interface Help {
	String topic() default "Unknown Topic";
	String url();
}

Default values for members can be specified in the annotation declaration so that it becomes optional when defining it. In the above example, String topic() default "Unknown Topic"; declares a default value for topic. While defining the Help annotation, the topic is optional and need not be specified. For example, @Help(url=".../help.html") is valid. The url must be specified, otherwise this will result in a compilation error.

In the annotation definition, the name=value pairs can be specified in any order. @Help(topic="Order does not matter", url=".../help.html") and @Help(url=".../help.html", topic="Order does not matter") are considered the same.

Pages: 1, 2, 3

Next Pagearrow