As another example of the sort of thing one could do with annotations, consider the familiar @deprecated tag. This tag does not affect the behavior of deprecated elements, but tells the compiler to emit warnings when annotated elements are used. The Javadoc tool also uses these annotations to generate appropriate documentation of the deprecated status of API elements. The deprecation facility was implemented in an ad hoc fashion using documentation comments, but could have been implemented atop a program annotation facility, had one existed at the time the deprecation facility was designed.
A second example of ad hoc program annotation already present in the Java platform is the transient modifier. The presence or absence of this modifier does not affect the semantics of a field, but it may be queried at runtime by the libraries that comprise the serialization subsystem as part of the serialization process. It is also queried at documentation generation time by the Javadoc utility as part of the process of generating documentation for the serialized form of the class.
Since many annotations will be used only by development tools such as stub generators, it makes little sense to retain all annotations at run time; doing so could increase run-time memory-footprint and harm performance. There are, however, some annotation types that are useful at run time, and some that are useful in tools that only have access to class files (not source files). Therefore, certain annotations are stored by the compiler in class file attributes (JVMS 4.7), and some of these annotations are then made available for inspection at runtime via new reflective APIs.
The program annotation facility consists of several parts:
An annotation type declaration takes the form of a highly restricted interface declaration. Annotation type declarations are legal wherever interface declarations are legal, and have the same scope and accessibility. To distinguish an annotation type declaration from an ordinary interface declaration, the keyword interface is replaced by @interface. Note that the at sign (@) and the keyword interface are two distinct tokens; technically it is possible to separate them with whitespace, but convention dictates that this is not done.
The following restrictions are imposed on annotation type declarations:
// Illegal self-reference!! @interface SelfRef { SelfRef value(); }and so is this:
// Illegal circularity!! @interface Ping { Pong value(); } @interface Pong { Ping value(); }
The interface created by an annotation type declaration inherits several additional members from java.lang.annotation.Annotation, which are the implicitly declared methods corresponding to the instance methods java.lang.Object (JLS 9.2). These methods are not, however, considered to be members of the annotation type, and it is illegal to use them in annotations.
Unlike method declarations in ordinary interfaces, a default value may be specified for an annotation type member. This is done by following its (empty) parameter list with the keyword default and the default value of the member. Note that defaults are applied dynamically at the time annotations are read; default values are not compiled into annotations. Thus, changing a default value affects annotations even in classes that were compiled before the change was made (presuming these annotations lack an explicit value for the defaulted member).
The grammar for an annotation type declaration is shown below:
AnnotationTypeDeclaration: InterfaceModifiersopt @ interface Identifier AnnotationTypeBody AnnotationTypeBody: { AnnotationTypeMemberDeclarationsopt } AnnotationTypeMemberDeclarations: AnnotationTypeMemberDeclaration AnnotationTypeMemberDeclarations AnnotationTypeMemberDeclaration AnnotationTypeMemberDeclaration: AbstractMethodModifiersopt Type Identifier ( ) DefaultValueopt ; ConstantDeclaration ClassDeclaration InterfaceDeclaration EnumDeclaration AnnotationTypeDeclaration ; DefaultValue: default MemberValueAll of the same restrictions apply to InterfaceModifiers and AbstractMethodModifiers as apply for ordinary interface declarations. (By convention, no AbstractMethodModifiers should be present.) The Type is restricted to primitive types, String, Class, enum types, annotation types, and arrays of the preceding types. It is permissible to use bounded wildcards to parameterize the Class return type, and the compiler must enforce such bounds on annotations. (See the JSR-14 specification for a description of bounded wildcards.) Note that this does not conflict with the prohibition on generic methods, as wildcards eliminate the need for an explicit type parameter.
A MemberValue is used to specify a default value, whose type must be appropriate for the member whose default is being specified. The detailed grammar and semantic restrictions on MemberValue are contained in Section III, below.
The following annotation type declaration defines an annotation type with several members:
// Normal annotation type declaration with several members /** * Describes the "request-for-enhancement" (RFE) that led to the presence of * the annotated API element. */ public @interface RequestForEnhancement { int id(); // Unique ID number associated with RFE String synopsis(); // Synopsis of RFE String engineer(); // Name of engineer who implemented RFE String date(); // Date RFE was implemented }The following annotation type declaration defines an annotation type with no members, termed a marker annotation type:
// Marker annotation type declaration /** * Annotation with this type indicates that the specification of the * annotated API element is preliminary and subject to change. */ public @interface Preliminary { }By convention, the name of the sole member in a single-member annotation type is value. (Linguistic support for this convention is provided by the single member annotation construct; you must obey the convention in order to make use of the construct.) The convention is illustrated in the following annotation type declaration:
// Single-member annotation type declaration /** * Associates a copyright notice with the annotated API element. */ public @interface Copyright { String value(); }The following annotation type declaration defines a single-member annotation type whose sole member has an array type:
// Single-member annotation type declaration with array-typed member /** * Associates a list of endorsers with the annotated class. */ public @interface Endorsers { String[] value(); }Here is an example of complex annotation types, annotation types that contain one or more members whose types are also annotation types.
// Complex Annotation Type /** * A person's name. This annotation type is not designed to be used * directly to annotate program elements, but to define members * of other annotation types. */ public @interface Name { String first(); String last(); } /** * Indicates the author of the annotated program element. */ public @interface Author { Name value(); } /** * Indicates the reviewer of the annotated program element. */ public @interface Reviewer { Name value(); }
The following annotation type declaration provides default values for two of its four members:
// Annotation type declaration with defaults on some members public @interface RequestForEnhancement { int id(); // No default - must be specified in each annotation String synopsis(); // No default - must be specified in each annotation String engineer() default "[unassigned]"; String date() default "[unimplemented]"; }
The following annotation type declaration shows a Class annotation whose value is restricted by a bounded wildcard.
// Annotation type declaration with bounded wildcard to restrict Class annotation /** * The annotation type declaration below presumes the existence of this interface, * which describes a formatter for Java programming language source code. */ public interface Formatter { ... } /** * Designates a formatter to pretty-print the annotated class. */ public @interface PrettyPrinter { Class<? extends Formatter> value(); }
Annotations may be used as modifiers in any declaration, whether class, interface, field, method, parameter, constructor, enum, or local variable. Annotations may also be used on enum constants (which are implicit field declarations). Such annotations are placed immediately before the enum constant they annotate.
Finally, annotations may be used on package declarations, with the restriction that at most one annotated package declaration is permitted for a given package. The manner in which this restriction is enforced must, of necessity, vary from implementation to implementation. The following scheme is strongly recommended for file-system-based implementations: The sole annotated package declaration, if it exists, is placed in a source file called "package-info.java" in the directory containing the source files for the package. This file does not contain the source for a class called package-info.java; indeed it would be illegal for it to do so, as package-info is not a legal identifier. Typically "package-info.java" contains only a package declaration, preceded immediately by the annotations on the package. While the file could technically contain the source code for one or more package-private classes, it would be very bad form.
It is recommended that package-info.java, if it is present, take the place of package.html for javadoc and other similar documentation generation systems. If this file is present, the documentation generation tool should ignore package.html and look instead for the package documentation comment immediately preceding the (possibly annotated) package declaration in package-info.java. In this way, package-info.java becomes the sole repository for package level annotations and documentation. If, in future, it becomes desirable to add any other package-level information, this file should prove a convenient home for this information.
The annotation type (interface) corresponding to an annotation must be accessible at the point where the annotation is used. A declaration cannot have multiple annotations for the same annotation type. Annotations are conventionally placed before all other modifiers, but this is not a requirement; they may be freely intermixed with other modifiers.
There are three kinds of annotations. The first (normal annotation) is fully general. The others (marker annotation and single-member annotation) are merely shorthands.
Annotation: NormalAnnotation MarkerAnnotation SingleMemberAnnotationA normal annotation is used to annotate a program element:
NormalAnnotation: @ TypeName ( MemberValuePairsopt ) MemberValuePairs: MemberValuePair MemberValuePairs , MemberValuePair MemberValuePair: SimpleName = MemberValue MemberValue: ConditionalExpression Annotation MemberValueArrayInitializer MemberValueArrayInitializer: { MemberValuesopt ,opt } MemberValues: MemberValue MemberValues , MemberValue
Note that the at-sign (@) is a token unto itself. Technically it is possible to put whitespace in between the at-sign and the TypeName, but convention dictates that this is not done.
TypeName may be a fully qualified or simple annotation type name. A simple name may be used to refer to an annotation type defined in another package if it is imported, either singly:
import PackageName.SimpleName;or on demand:
import PackageName.*;These are normal import declarations (JLS 7.5.1, 7.5.2): annotation types are imported in the same fashion as classes and interfaces. Any annotation types defined in java.lang would be implicitly imported on demand.
The SimpleName in a MemberValuePair must be the name of one of the members of the annotation type identified by Name in the containing annotation. (In other words, the simple name in a member-value pair must also be a method name in the interface identified by Name.) The return type of this method defines the member type of the member-value pair.
If the member type is not an annotation type or an array type, MemberValue must be a ConditionalExpression (JLS 15.25) whose type is assignment compatible (JLS 5.2) with the member type. (A ConditionalExprression is simply an expression without assignments, and not necessarily an expression involving the conditional operator (? :).) If member type is a primitive type or String, the ConditionalExpression must be a constant expression (JLS 15.28). If the member type is Class, the value must be a class literal (JLS 15.8.2). If the member type is an enum type, the value must be the simple (unqualified) name of an enum constant. Loosely speaking, no matter what the member type, the value must be a compile-time constant. Note that null is not a legal member value for any member type.
If the member type is an annotation type, the MemberValue must be an Annotation with the same type.
If the member type is an array type, the MemberValue may be either a MemberValueArrayInitializer or a MemberValue that is legal for the array's element type (as defined above). A MemberValueArrayInitializer is similar to a normal array initializer (JLS 10.6), except that annotations are permitted in place of expressions. Each MemberValueInitializer (analogous to a variable initializer in an array initializer) must be a legal MemberValue for the array's element type (as defined above).
If the member type is an array type and the corresponding MemberValue is not a MemberValueArrayInitializer, an array value whose sole element is the value represented by the MemberValue is associated with the member. In other words, it is permissible to omit the curly braces when a single-element array is to be associated with an array-valued annotation type member.
Note that the array's element type cannot be an array type, that is, nested array types are not permitted as member types. (While the annotation syntax would permit this, the annotation type declaration syntax would not.)
Note that it is possible to annotate an annotation type declaration. Such annotations are known as meta-annotations. An annotation type may be used to annotate its own declaration. More generally, circularities in the transitive closure of the "annotates" relation are permitted. For example, it is legal to annotate an annotation type declaration with another annotation type, and to annotate the latter type's declaration with the former type. (The standard meta-annotation types contain several such circularities.)
Here is an example of a normal annotation:
// Normal annotation @RequestForEnhancement( id = 2868724, synopsis = "Provide time-travel functionality", engineer = "Mr. Peabody", date = "4/1/2004" ) public static void travelThroughTime(Date destination) { ... }Note that the types of the annotations in the examples in this section are the annotation types defined in the examples in Section II.
The second form of annotation, marker annotation, is a shorthand designed for use with marker annotation types:
MarkerAnnotation: @ TypeNameIt is simply a shorthand for the normal annotation:
@TypeName()Example:
// Marker annotation @Preliminary public class TimeTravel { ... }Note that it is legal to use marker annotations for annotation types with members, so long as all the members have default values.
The third form of annotation, single-member annotation, is a shorthand designed for use with single-member annotation types:
SingleMemberAnnotation: @ TypeName ( MemberValue )It is shorthand for the normal annotation:
@TypeName ( value = MemberValue )Example:
// Single-member annotation @Copyright("2002 Yoyodyne Propulsion Systems, Inc., All rights reserved.") public class OscillationOverthruster { ... }Example with array-valued single-member annotation:
// Array-valued single-member annotation @Endorsers({"Children", "Unscrupulous dentists"}) public class Lollipop { ... }Example with single-element array-valued single-member annotation (note that the curly braces are omitted):
// Single-element array-valued single-member annotation @Endorsers("Epicurus") public class Pleasure { ... }Example with complex annotation:
// Single-member complex annotation @Author(@Name(first = "Joe", last = "Hacker")) public class BitTwiddle { ... }Note that it is legal to use single-member annotations for annotation types with multiple members, so long as one member is named value, and all other members have default values.
Here is an example of an annotation that takes advantage of default values:
// Normal annotation with default values @RequestForEnhancement( id = 4561414, synopsis = "Balance the federal budget" ) public static void balanceFederalBudget() { throw new UnsupportedOperationException("Not implemented"); }
Here is an example of an annotation with a Class member whose value is restricted by the use of a bounded wildcard.
// Single-member annotation with Class member restricted by bounded wildcard // The annotation presumes the existence of this class. class GorgeousFormatter implements Formatter { ... } @PrettyPrinter(GorgeousFormatter.class) public class Petunia { ... } // This annotation is illegal, as String is not a subtype of Formatter!! @PrettyPrinter(String.class) public class Begonia { ... }
The ElementType enumerated type is used in conjunction with the Target meta-annotation type to indicate where an annotation type may be used:
/** * A program element type. The constants of this enumerated type * provide a simple classification of the declared elements in a * Java program. * * These constants are used in conjunction with the {@link Target} * meta-annotation type to specify where it is legal to use an * annotation type. */ public enum ElementType { /** Class, interface or enum declaration */ TYPE, /** Field declaration (includes enum constants) */ FIELD, /** Method declaration */ METHOD, /** Parameter declaration */ PARAMETER, /** Constructor declaration */ CONSTRUCTOR, /** Local variable declaration */ LOCAL_VARIABLE, /** Annotation type declaration */ ANNOTATION_TYPE, /** Package declaration */ PACKAGE } /** * Indicates the kinds of program element to which an annotation type * is applicable. If a Target meta-annotation is not present on an * annotation type declaration, the declared type may be used on any * program element. If such a meta-annotation is present, the compiler * will enforce the specified usage restriction. * * For example, this meta-annotation indicates that the declared type is * itself a meta-annotation type. It can only be used on annotation type * declarations: * * @Target(ANNOTATION_TYPE) * public @interface MetaAnnotationType { * ... * } * * This meta-annotation indicates that the declared type is intended solely * for use as a member type in complex annotation type declarations. It * cannot be used to annotate anything directly: * * @Target({}) * public @interface MemberType { * ... * } * * It is a compile-time error for a single ElementType constant to * appear more than once in a Target annotation. For example, the * following meta-annotation is illegal: * * // This annotation is illegal! * @Target({FIELD, METHOD, FIELD}) * public @interface Bogus { * ... * } */ @Documented @Retention(RUNTIME) @Target(ANNOTATION_TYPE) public @interface Target { ElementType[] value; }Note that the Target annotation type's own declaration exemplifies its use, as well as the use of the Documented meta-annotation type, shown below.
The Documented meta-annotation type is used to indicate that annotations with a type should be documented:
/** * Indicates that annotations with a type are to be documented by javadoc * and similar tools by default. This type should be used to annotate the * declarations of types whose annotations affect the use of annotated * elements by their clients. If a type declaration is annotated with * Documented, its annotations become part of the public API * of the annotated elements. */ @Documented @Target(ANNOTATION_TYPE) public @interface Documented { }The RetentionPolicy enumerated type is used in conjunction with the Retention meta-annotation type to indicate how long an annotation is to be retained:
/** * Annotation retention policy. The constants of this enumerated type * describe the various policies for retaining annotations. They are used * in conjunction with the {@link Retention} meta-annotation type to specify * how long annotations are to be retained. */ public enum RetentionPolicy { /** * Annotations are to be discarded by the compiler. */ SOURCE, /** * Annotations are to be recorded in the class file by the compiler * but need not be retained by the VM at run time. This is the default * behavior. */ CLASS, /** * Annotations are to be recorded in the class file by the compiler and * retained by the VM at run time, so they may be read reflectively. * * @see java.lang.reflect.AnnotatedElement */ RUNTIME } /** * Indicates how long annotations with the annotated type are to * be retained. If no Retention annotation is present on * an annotation type declaration, the retention policy defaults to * CLASS. * * A Target meta-annotation has effect only if the meta-annotated * type is use directly for annotation. It has no effect if the meta-annotated * type is used as a member type in another annotation type. */ @Documented @Retention(RUNTIME) @Target(ANNOTATION_TYPE) public @interface Retention { RetentionPolicy value(); }The Inherited meta-attribute is used to indicate that annotations with a type are to be automatically inherited by subclasses or subclass methods:
/** * Indicates that an annotation type is automatically inherited. If * an Inherited meta-annotation is present on an annotation type * declaration, and the user queries the annotation type on a class * declaration, and the class declaration has no annotation for this type, * then the class's superclass will automatically be queried for the * annotation type. This process will be repeated until an annotation for this * type is found, or the top of the class hierarchy (Object) * is reached. If no superclass has an annotation for this type, then * the query will indicate that the class in question has no such annotation. * * If an Inherited meta-annotation is present on an annotation type * declaration, and the user queries the annotation type on an instance method * declaration, and the declaration has no annotation for this type, then * the query method will automatically search for a superclass method that is * overridden by the annotated method. If such a method is found, it will be * queried for this annotation type. This process will be repeated until an * annotation for this type is found or the top of the class hierarchy * (Object) is reached. If the top of the class hierarchy is reached * without finding an annotation for this type, then the query will indicate * that the method in question has no such annotation. * * Note that this meta-annotation type has no effect if the annotated * type is used to annotate anything other than a class or instance method * declaration. Note also that this meta-annotation only causes annotations * to be inherited from superclasses; annotations on implemented interfaces * have no affect. */ @Documented @Retention(RUNTIME) @Target(ANNOTATION_TYPE) public @interface Inherited { }
Programmers occasionally overload a method declaration when they mean to override it. The classic example concerns the equals method. Programmers write the following:
public boolean equals(Foo that) { ... }when they mean to write:
public boolean equals(Object that) { ... }This is perfectly legal, but class Foo inherits the equals implementation from Object, which can cause some very subtle bugs. The following standard annotation type provides a way to avoid this trap:
package java.lang; /** * Indicates that a method declaration is intended to override a concrete * method declaration in a superclass. If a method is annotated with * this type but does not override a superclass method, compilers are * required to generate an error message. */ @Retention(SOURCE) @Target(METHOD) public @interface Overrides { }This annotation type allows the programmer to declare his belief that a method declaration overrides a superclass method:
@Overrides public boolean equals(Foo that) { ... }The compiler checks whether method actually overrides a superclass method, and reports an error if it does not, nipping the problem in the bud.
Note that this annotation type is not a fundamental part of this specification, but an initial use of the facility in the Java programming language.
/** * Represents an annotated element of the program currently running in this * JVM. This interface allows annotations to be read reflectively. All * annotations returned by methods in this interface are serializable. * * If an annotation object returned by a method in this interface * contains (directly or indirectly) one or more Class members * that refer to a class that is not accessible in this JVM, attempting * to read the class by calling the relevant Class-returning * method on the returned annotation (or an annotation referred to by * that annotation) will cause a {@link TypeNotPresentException} * to be thrown. */ public interface AnnotatedElement { /** * Returns true if an annotation for the specified type * is present on this element, else false. This method * is designed primarily for convenient access to marker annotations. * * @param annotationType the Class object corresponding to the * annotation type * @return true if an annotation for the specified annotation * type is present on this element, else false * @throws NullPointerException if annotationType is null */ boolean isAnnotationPresent(Class<? extends Annotation> annotationType); /** * Returns this element's annotation for the specified type if * such an annotation is present, else null. * * @param annotationType the Class object corresponding to the * annotation type * @return this element's annotation for the specified annotation type if * present on this element, else null * @throws NullPointerException if annotationType is null */ <T extends Annotation> T getAnnotation(Class<T> annotationType); /** * Returns all annotations present on this element. (Returns an array * of length zero if this element has no annotations.) * * @return all annotations present on this element */ Annotation[] getAnnotations(); /** * Returns all annotations that are directly present on this * element. Unlike the other methods in this interface, this method * ignores inherited annotations. (Returns an array of length zero if * no annotations are directly present on this element.) * * @return All annotations directly present on this element */ Annotation[] getDeclaredAnnotations(); }To allow annotations on parameters to be read, a new method is added to java.lang.reflect.Method and java.lang.reflect.Constructor.
/** * Returns an array of arrays that represent the annotations on the formal * parameters, in declaration order, of the method represented by * this Method object. (Returns an array of length zero if the * underlying method is parameterless.) The annotation objects contained * in the returned arrays are serializable. * * @returns an array of arrays that represent the annotations on the formal * parameters, in declaration order, of the method represented by this * Method object */ public Annotation[][] getParameterAnnotations();The following exception is added to java.lang:
/**
* Thrown when an application tries to access a type using a string
* representing the type's name, but no definition for the type with
* the specified name can be found. This exception differs from
* {@link ClassNotFoundException} in that ClassNotFoundException
* is a checked exception, whereas this exception is unchecked.
*
* Note that this exception may be used when undefined type variables
* are accessed as well as when types (e.g., classes, interfaces or
* annotation tyes) are loaded.
*/
public class TypeNotPresentException extends RuntimeException {
/**
* Constructs a TypeNotPresentException
for the named type
* with the specified cause.
*
* @param typeName the fully qualified name of the unavailable type
* @param cause the exception that was thrown when the system attempted to
* load the named type, or null if unavailable or inapplicable
*/
public TypeNotPresentException(String typeName, Throwable cause);
/**
* Returns the fully qualified name of the unavailable type.
*
* @return the fully qualified name of the unavailable type
*/
public String typeName();
}
Here are examples of how these APIs can be used to read the annotations shown in Section III:
// Reading a normal annotation Method m = TimeTravel.class.getMethod("travelThroughTime", new Class[] {Date.class}); RequestForEnhancement rfe = m.getAnnotation(RequestForEnhancement.class); int id = rfe.id(); String synopsis = rfe.synopsis(); String engineer = rfe.engineer(); String date = rfe.date(); // Reading a marker annotation boolean isPreliminary = TimeTravel.class.isAnnotationPresent(Preliminary.class); // Reading a single-member annotation String copyrightHolder = OscillationOverthruster.class.getAnnotation(Copyright.class).value(); // Reading an array-valued single-member annotation String[] endorsers = Lollipop.class.getAnnotation(Endorsers.class).value(); // Reading a single-member complex annotation Name author = BitTwiddle.class.getAnnotation(Author.class).value(); String firstName = author.first(); String lastName = author.last();The preceding APIs should also suffice for "specific tools" (which are willing to load annotation interfaces into the virtual machine but not annotated classes), assuming the existence of a tool that reads a class file without loading the class, and returns a "class mirror" object that implements AnnotatedElement. General tools, however, will need to read annotaions using an "at-arms-length" API, which allows annotations to be read without loading the annotated classes or annotation interfaces into the virtual machine.
One more method is added to the core reflection API. In order to distinguish an annotation type from a normal interface at run time, the following method is added to java.lang.Class:
/** * Returns true if this Class object represents an annotation type. * Note that if this method returns true, {@link #isInterface()} would also * return true, as all annotation types are also interfaces. * * @returns true if this class object represents an annotation type; * false otherwise */ public boolean isAnnotationType();
package java.lang.annotation; /** * Marker interface extended by every annotation interface. */ public interface Annotation { /** * Returns true if the specified object represents an annotation * that is logically equivalent to this one. In other words, * returns true if the specified object is an instance of the same * annotation type as this instance, all of whose members are equal * to the corresponding member of this annotation, as defined below: * * Two corresponding primitive typed members whose values are * x and y are considered equal if x == y, * unless their type is float or double. * * Two corresponding float members whose values * are x and y are considered equal if * Float.valueOf(x).equals(Float.valueOf(y)). * (Unlike the == operator, NaN is considered equal * to itself, and 0.0f unequal to -0.0f.) * * Two corresponding double members whose values * are x and y are considered equal if * Double.valueOf(x).equals(Double.valueOf(y)). * (Unlike the == operator, NaN is considered equal * to itself, and 0.0 unequal to -0.0.) * * Two corresponding String, Class, enum, or * annotation typed members whose values are x and y * are considered equal if x.equals(y). (Note that this * definition is recursive for enum typed members.) * * Two corresponding array typed members x and y * are considered equal if Arrays.equals(x, y), for the * appropriate overloading of {@link java.util.Arrays#equals}. * * * @return true if the specified object represents an annotation * that is logically equivalent to this one, otherwise false */ boolean equals(Object obj); /** * Returns a string representation of this annotation. The details * of the representation are implementation-dependent, but the following * may be regarded as typical: * * @com.acme.util.Name(first=Alfred, middle=E., last=Neuman) * * * @return a string representation of this annotation */ String toString(); }In order to implement the equals, hashCode, and toString for annotations, we need content-based equals, hashCode, and toString methods for arrays. The utility class java.util.Arrays currently contains the equals methods, but not the others. Thus, we add the following methods to java.util.Arrays:
// hashCode methods /** * Returns a hash code based on the contents of the specified array. * For any two long arrays a and b * such that Arrays.equals(a, b), it is also the case that * Arrays.hashCode(a) == Arrays.hashCode(b). * * The value returned by this method is the same value that would be * obtained by invoking the {@link List#hashCode() hashCode} * method on a {@link List} containing a sequence of {@link Long} * instances representing the elements of a in the same order. * If a is null, this method returns 0. * * @param the array whose hash value to compute * @return a content-based hash code for the array a */ public static int hashCode(long a[]); // Analogous methods for each of the other seven primitive types (not shown) // plus these two for Object: /** * Returns a hash code based on the contents of the specified array. If * the array contains other arrays as elements, the hash code is based on * their identities rather than their contents. It is therefore * acceptable to invoke this method on an array that contains itself as an * element, either directly or indirectly through one or more levels of * arrays. * * For any two arrays a and b such that * Arrays.equals(a, b), it is also the case that * Arrays.hashCode(a) == Arrays.hashCode(b). * * The value returned by this method is equal to the value that would * be returned by Arrays.asList(a).hashCode(), unless a * is null, in which case 0 is returned. * * @param the array whose content-based hash code to compute * @return a content-based hash code for the array a * @see #deepHashCode(Object[]) */ public static int hashCode(Object a[]); /** * Returns a hash code based on the "deep contents" of the specified * array. If the array contains other arrays as elements, the * hash code is based on their contents and so on, ad infinitum. * It is therefore unacceptable to invoke this method on an array that * contains itself as an element, either directly or indirectly through * one or more levels of arrays. The behavior of such an invocation is * undefined. * * For any two arrays a and b such that * Arrays.deepEquals(a, b), it is also the case that * Arrays.deepHashCode(a) == Arrays.deepHashCode(b). * * The computation of the value returned by this method is similar to * that of the value returned by {@link List#hashCode()} on a list * containing the same elements as a in the same order, with one * difference: If an element e of a is itself an array, * its hash code is computed not by calling e.hashCode(), but as * by calling the appropriate overloading of Arrays.hashCode(e) * if e is an array of a primitive type, or as by calling * Arrays.deepHashCode(e) recursively if e is an array * of a reference type. If a is null, this method * returns 0. * * @param the array whose deep-content-based hash code to compute * @return a deep-content-based hash code for the array a * @see #hashCode(Object[]) */ public static int deepHashCode(Object a[]); // toString methods /** * Returns a string representation of the contents of the specified array. * The string representation consists of a list of the array's elements, * enclosed in square brackets ("[]"). Adjacent elements are * separated by the characters ", " (a comma followed by a * space). Elements are converted to strings as by * String.valueOf(long). Returns "null" if a * is null. * * @param the array whose string representation to return * @return a string representation of the array a */ public static String toString(long[] a); // Analogous methods for each of the other seven primitive types (not shown) // plus these two for Object: /** * Returns a string representation of the contents of the specified array. * If the array contains other arrays as elements, they are converted to * strings by the {@link Object#toString} method inherited from * Object, which describes their identities rather than * their contents. It is therefore acceptable to invoke this method on an * array that contains itself as an element, either directly or indirectly * through one or more levels of arrays. * * The value returned by this method is equal to the value that would * be returned by Arrays.asList(a).toString(), unless a * is null, in which case "null" is returned. * * @param the array whose string representation to return * @return a string representation of the array a * @see #deepToString() */ public static String toString(Object[] a); /** * Returns a string representation of the "deep contents" of the specified * array. If the array contains other arrays as elements, the string * representation contains their contents and so on, ad infinitum. If * the specified array contains itself as an element, the element will be * converted to the string "(this array)". If, however, the * specified array contains an indirect reference to itself through one or * more levels of arrays, the behavior of this method is undefined. * * The string representation consists of a list of the array's * elements, enclosed in square brackets ("[]"). Adjacent * elements are separated by the characters ", " (a comma * followed by a space). Elements are converted to strings as by * String.valueOf(Object), unless they are themselves * arrays. * * If an element e is an array of a primitive type, it is * converted to a string as by invoking the appropriate overloading of * Arrays.toString(e). If an element e is an array of a * reference type, it is converted to a string as by invoking * Arrays.deepToString(e) recursively, unless e * is the specified array itself (in which case it is converted to * the string "(this array)"). * * This method returns "null" if the specified array * is null. * * @param the array whose string representation to return * @return a string representation of the array a * @see #toString(Object[]) */ public static String deepToString(Object[] a); // This equals method goes with deepHashCode. (The other eight equals methods // are already in java.util.Arrays.) /** * Returns true if the two specified arrays are deeply * equal to one another. Unlike the @link{#equals{Object[],Object[]) * method, this method is appropriate for use with nested arrays of * arbitrary depth. * * Two array references are considered deeply equal if both * are null, or if they refer to arrays that contain the same * number of elements and all corresponding pairs of elements in the two * arrays are deeply equal. * * Two possibly null elements e1 and e2 are * deeply equal if any of the following conditions hold: * * e1 and e2 are both arrays of object reference * types, and Arrays.deepEquals(e1, e2) would return true * * e1 and e2 are arrays of the same primitive * type, and the appropriate overloading of * Arrays.equals(e1, e2) would return true. * * e1 == e2 * * e1.equals(e2) would return true. * * Note that this definition permits null elements at any depth. * * If either of the specified arrays contain themselves as elements * either directly or indirectly through one or more levels of arrays, * the behavior of this method is undefined. * * @param a1 one array to be tested for equality * @param a2 the other array to be tested for equality * @return true if the two arrays are equal * @see #equals(Object[],Object[]) */ public static boolean deepEquals(Object[] a1, Object[] a2);
/** * Thrown to indicate that a program has attempted to access a member of * an annotation type that was added to the annotation type definition after * the annotation was compiled (or serialized). This error will not be thrown * if the new member has a default value. */ public class IncompleteAnnotationError extends IncompatibleClassChangeError { /** * Constructs an IncompleteAnnotationError to indicate that * the named member was missing from the specified annotation type. * * @param annotationType the Class object for the annotation type * @param memberName the name of the missing member */ public IncompleteAnnotationError(Class annotationType, String memberName) { } /** * Returns the Class object for the annotation type with the * missing member. * * @return the Class object for the annotation type with the * missing member */ public Class annotationType() { } /** * Returns the name of the missing member. * * @return the name of the missing member */ public Class memberName() { } }
/** * Thrown to indicate that a program has attempted to access a member of * an annotation type whose type has changed after the annotation was compiled * (or serialized). */ public class AnnotationTypeMismatchError extends IncompatibleClassChangeError { /** * Constructs an AnnotationTypeMismatchError to indicate that * the type of the named member has changed. * * @param annotationType the Class object for the annotation type * @param memberName the name of the incorrectly typed member */ public AnnotationTypeMismatchError(Class annotationType, String memberName) { } /** * Returns the Class object for the annotation type with the * incorrectly typed member. * * @return the Class object for the annotation type with the * incorrectly typed member */ public Class annotationType() { } /** * Returns the name of the incorrectly typed member. * * @return the name of the incorrectly typed member */ public Class memberName() { } }Changing the type of an annotation type member may cause compilation errors for existing classes that contain annotations for the type, or that invoke the accessor for the member. Class files that invoke the accessor will fail at runtime with a java.lang.NoSuchMethodError.
Changing the retention policy from SOURCE to CLASS or RUNTIME will behave as expected: existing class files will behave as if their sources contained no annotations for the type in question. The source must be recompiled to propagate the annotation into the class file. Changing the retention policy from RUNTIME to CLASS or SOURCE works fine. (If the new value of the meta-annotation is SOURCE, class files containing annotations for the type will behave as if those annotations were not present; if the new value is CLASS, the these annotations will not be readable at runtime.) Changing the retention policy from CLASS to SOURCE or RUNTIME behaves exactly as adding a SOURCE or RUNTIME meta-annotation when none was present.
The RuntimeVisibleAnnotations attribute has the following format:
RuntimeVisibleAnnotations_attribute { u2 attribute_name_index; u4 attribute_length; u2 num_annotations; annotation annotations[num_annotations]; }The items of the RuntimeVisibleAnnotations structure are as follows:
The annotation structure has the following format:
annotation { u2 type_index; u2 num_member_value_pairs; { u2 member_name_index; member_value value; } member_value_pairs[num_member_value_pairs]; }
The items of the annotation structure are as follows:
The member_value structure is a discriminated union representing the value of a member-value pair. It is used to represent values in all class file attributes that describe annotations ( RuntimeVisibleAnnotations, RuntimeInvisibleAnnotations, RuntimeVisibleParameterAnnotations, and RuntimeInvisibleParameterAnnotations).
The member_value structure has the following format:
member_value { u1 tag; union { u2 const_value_index; { u2 type_name_index; u2 const_name_index; } enum_const_value; u2 class_info_index; annotation annotation_value; { u2 num_values; member_value values[num_values]; } array_value; } value; }
The items of the member_value structure are as follows:
tag value | Member Type |
---|---|
's' | String |
'e' | enum constant |
'c' | class |
'@' | annotation type |
'[' | array |
The RuntimeInvisibleAnnotations attribute is a variable length attribute in the attributes table of the ClassFile, field_info, and method_info structures. The RuntimeInvisibleAnnotations attribute records runtime-invisible Java programming language annotations on the corresponding class, method, or field. Each ClassFile, field_info, and method_info structure may contain at most one RuntimeInvisibleAnnotations attribute, which records all the runtime-invisible Java programming language annotations on the corresponding program element.
The RuntimeInvisibleAnnotations attribute has the following format:
RuntimeInvisibleAnnotations_attribute { u2 attribute_name_index; u4 attribute_length; u2 num_annotations; annotation annotations[num_annotations]; }The items of the RuntimeInvisibleAnnotations structure are as follows:
The RuntimeVisibleParameterAnnotations attribute has the following format:
RuntimeVisibleParameterAnnotations_attribute { u2 attribute_name_index; u4 attribute_length; u1 num_parameters; { u2 num_annotations; annotation annotations[num_annotations]; } parameter_annotations[num_parameters]; }The items of the RuntimeVisibleParameterAnnotations structure are as follows:
The RuntimeInvisibleParameterAnnotations attribute is a variable length attribute in the attributes table of the method_info structure. The RuntimeInvisibleParameterAnnotations attribute records runtime-invisible Java programming language annotations on the parameters of the corresponding method. Each method_info structure may contain at most one RuntimeInvisibleParameterAnnotations attribute, which records all the runtime-invisible Java programming language annotations on the parameters of the corresponding method.
The RuntimeInvisibleParameterAnnotations attribute has the following format:
RuntimeInvisibleParameterAnnotations_attribute { u2 attribute_name_index; u4 attribute_length; u1 num_parameters; { u2 num_annotations; annotation annotations[num_annotations]; } parameter_annotations[num_parameters]; }The items of the RuntimeInvisibleParameterAnnotations structure are as follows:
The AnnotationDefault attribute has the following format:
AnnotationDefault_attribute { u2 attribute_name_index; u4 attribute_length; member_value default_value; }The items of the AnnotationDefault structure are as follows:
Package-level annotations are a special case. When the compiler encounters an annotated package declaration, it must emit a synthetic interface named package-name.package-info with no superinterfaces or members (exculding those methods implicitly declared as per JLS 9.2). The synthetic interface has the default access level ("package-private"). The package-level annotations are stored in the RuntimeVisibleAnnotations and RuntimeInvisibleAnnotations attributes of the ClassFile structure.
The Doclet API is not currently part of any standard. This (non-normative) appendix shows how the Doclet API will be modified to support program annotations. Note that this API is rather low-level: it ignores member defaults and annotation inheritance. This is consistent with rest of the Doclet API.
The following interfaces are added to com.sun.javadoc:
/** * Represents an annotation. * An annotation associates a value with each member of an annotation type. */ public interface AnnotationDesc { /** * Returns the annotation type of this annotation. * * @return the annotation type of this annotation */ AnnotationTypeDoc annotationType(); /** * Returns this annotation's members and their values. * Only those explicitly present in the annotation are * included, not those assuming their default values. * Returns an empty array if there are none. * * @return this annotation's members and their values */ MemberValuePair[] memberValues(); /** * Represents an association between an annotation type member * and one of its values. */ public interface MemberValuePair { /** * Returns the annotation type member. * * @return the annotation type member */ AnnotationTypeMemberDoc member(); /** * Returns the value associated with the annotation type member. * * @return the value associated with the annotation type member */ AnnotationValue value(); } } /** * Represents an annotation type. */ public interface AnnotationTypeDoc extends ClassDoc { /** * Returns the members of this annotation type. * Returns an empty array if there are none. * * @return the members of this annotation type */ AnnotationTypeMemberDoc[] members(); } /** * Represents a member of an annotation type. */ public interface AnnotationTypeMemberDoc extends MethodDoc { /** * Returns the default value of this member. * Returns null if this member has no default. * * @return the default value of this member */ AnnotationValue defaultValue(); } /** * Represents a value of an annotation type member. */ public interface AnnotationValue { /** * Returns the value. * The type of the returned object is one of the following: * * - a wrapper class for a primitive type * -Several existing doclet interfaces are modified as follows:String
* -ClassDoc
* -FieldDoc
(representing an enum constant) * -AnnotationDesc
* -AnnotationValue[]
* * @return the value */ Object value(); /** * Returns a string representation of the value. * * @return the text of a Java language annotation value expression * whose value is the value of this annotation type member */ String toString(); }
======================================== Doc ======================================== Add methods: /** * Is this Doc item an annotation type? * * @return true if it represents an annotation type */ boolean isAnnotationType(); /** * Is this Doc item an annotation type member? * * @return true if it represents an annotation type member */ boolean isAnnotationTypeMember(); ======================================== PackageDoc ======================================== Add methods: /** * Get included annotation types in this package. * * @return included annotation types in this package */ AnnotationTypeDoc[] annotationTypes(); /** * Get the annotations of this package. * Return an empty array if there are none. * * @return the annotations of this package */ AnnotationDesc[] annotations(); Clarify that interfaces() excludes annotation types. ======================================== ClassDoc ======================================== Clarify that methods() and methods(boolean) exclude annotation type members. ======================================== Parameter ======================================== Add method: /** * Get the annotations of this parameter. * Return an empty array if there are none. * * @return the annotations of this parameter */ AnnotationDesc[] annotations(); ======================================== ProgramElementDoc ======================================== Add method: /** * Get the annotations of this program element. * Return an empty array if there are none. * * @return the annotations of this program element */ AnnotationDesc[] annotations(); Clarify that the string returned by modifiers() does not include annotations. ======================================== Type ======================================== Add method: /** * Return this type as anAnnotationTypeDoc
if it represents * an annotation type. * * @return anAnnotationTypeDoc
if the type is an annotation * type, or null if it is not */ AnnotationTypeDoc asAnnotationTypeDoc();
Given the huge number of Java programs already in existence, there is a great advantage to providing source compatibility. Many programs use attribute as an identifier, and those programs would no longer be legal. If fact, it would not have been possible to do this, as it conflicts with javax.print.attribute.
Two reasons: (1) It is reminiscent of Javadoc tags, a preexisting ad hoc annotation facility in the Java programming language, and (2) It is mnemonic: at = a[nnotation] t[ype].
Because annotation type declarations really do declare an interface (which is used to access the annotations). Thus, it makes sense that annotation types and ordinary interfaces share a common declaration syntax. Further, the field-like notation would be confusing because the semantics are different from fields. In particular, the default semantics are different from field initialization semantics.
It complicates the attribute type system, and makes it much more difficult to write "Specific Tools" (per the taxonomy in Section VI).
At first glance it would appear that you could never annotate with such a type, so the prohibition might seem unnecessary, but you can get yourself into trouble if you also provide a default:
@interface Foo { Foo foo() default @Foo; } @Foo int i;This is legal, but the resulting annotation is self-referential (or infinite), as well as totally useless.
It is consistent with the fact that null is not a compile-time constant expression (JLS 15.28). Also it's consistent with the proposal that null is not a legal case label in a switch statement on an enum expression. It eliminates a behavioral difference between primitive annotation members and reference type annotation members, and reduces the likelihood of a NullPointerException at run time.
A similar effect is available by annotating once with an annotation type whose sole member is of an array type. The resulting system is simpler and cleaner: you don't have to worry about whether you're going to get back a single instance of the annotation type or multiple instances when you query an annotation. Also it's consistent with the usual restriction on modifiers.
This would greatly complicate the annotation syntax: We would have to sacrifice the simplicity of saying that annotations are simply modifiers, which can be used on declarations.
To eliminate a syntactic ambiguity involving assignment operators.
There's no place to put them. The class file format does not describe or expose structure within a method. If, in future, the class file format is redesigned to provide such structural information, this decision will be reconsidered.
The compiler already tells you if you've failed to override an interface method (assuming you implement the interface). It would be redundant and confusing to offer similar functionality with the Overrides annotation.
For many annotations, this behavior is exactly what you want, as the annotation results in the production of some artifact that is required to run the program (such as an additional source file, or an XML document). Some annotations, however, are merely "structured comments," which are not intended to have any affect on the running program. For example, a UML modeling tool might annotate program elements with model information for its own use. If the compiler knew that a given annotation were of this character, it could safely ignore the annotation even if the class file for its annotation type were unavailable. This would allow people to compile the program even if they did not have access to the class file for the annotation type.
Unfortunately there is no way to indicate that a given annotation type is of this character. It cannot be done with a meta-annotation, as the compiler cannot read the meta-annotation if it does not have access to the annotation type.
There are other alternatives. For example, we could provide a second annotation syntax (say, @@(a=1, b=3)) that indicates to the compiler that an annotation can be ignored if its class file is not present. However, this places the decision as to whether an annotation should be ignored in the hands of the annotater, rather than the annotation type author. It should be up to the author of an annotation type to decide whether annotations of that type can be safely ignored.
A similar idea is to provide a compiler flag that instructs the compiler to ignore certain annotations. Again, this idea is questionable, as it places the decision in the wrong hands. This alternative, as well as the previous one, could result in programs that simply did not run if the programmer told the compiler to ignore critical metadata.
Do you consider it problematic that annotations tie you to annotation types in the same way that method invocations tie you libraries? If so, can you think of an acceptable solution to the problem?
Copyright 2002-2003 Sun Microsystems, Inc., 901 San Antonio Road, Palo Alto, California 94303 U.S.A. All rights reserved.