`
leonzhx
  • 浏览: 788604 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论

Chapter 20. Annotations -- Thinking in Java

阅读更多

1) Annotations (also known as metadata) provide a formalized way to add information to your code so that you can easily use that data at some later point.

 

2) The syntax of annotations consists mainly of the addition of the @ symbol to the language. Java SE5 contains three general-purpose built-in annotations, defined in java.lang :
    @Override , to indicate that a method definition is intended to override a method in the base class. This generates a compiler error if you accidentally misspell the method name or give an improper signature.
    @Deprecated , to produce a compiler warning if this element is used.
    @SuppressWarnings , to turn off inappropriate compiler warnings. This annotation is allowed but not supported in earlier releases of Java SE5 (it was ignored).

Four additional annotation types support the creation of new annotations.

 

3) Syntactically, annotations are used in much the same way as modifiers. Annotation definitions look a lot like interface definitions. In fact, they compile to class files like any other Java interface:

 

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

 

An annotation definition also requires the meta-annotations @Target and @Retention. @Target defines where you can apply this annotation (a method or a field, for example). @Retention defines whether the annotations are available in the source code (SOURCE ), in the class files (CLASS ), or at run time (RUNTIME ). An annotation without any elements, such as @Test above, is called a marker annotation.

 

4) Annotations will usually contain elements to specify values in your annotations. A program or tool can use these parameters when processing your annotations. Elements look like interface methods, except that you can declare default values:

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface UseCase {
    public int id();
    public String description() default "no description";
}

 

The values of the annotation elements are expressed as name-value pairs in parentheses after the @UseCase declaration:

 

public class PasswordUtils {
    @UseCase(id = 47, description = "Passwords must contain at least one numeric")
    public boolean validatePassword(String password) {
        return (password.matches("\\w*\\d\\w*"));
    }
    @UseCase(id = 48)
    public String encryptPassword(String password) {
        return new StringBuilder(password).reverse().toString();
    }
    @UseCase(id = 49, description = "New passwords can’t equal previously used ones")
    public boolean checkForNewPassword( List<String> prevPasswords, String password) {
            return !prevPasswords.contains(password);
    }
}

 

 

5) There are currently only three standard annotations (described earlier) and four meta-annotations defined in the Java language. The meta-annotations are for annotating annotations:


 

6) Without tools to read them, annotations are hardly more useful than comments. An important part of the process of using annotations is to create and use annotation processors. Java SE5 provides extensions to the reflection API to help you create these tools. It also provides an external tool called apt to help you parse Java source code with annotations.

 

7) The method Method.getAnnotation which comes from the AnnotatedElement interface (classes like Class , Method and Field all implement this interface) returns the annotation object of the specified type. If there are no annotations of that particular type on the annotated method, a null value is returned.

 

8) Here is a list of the allowed types for annotation elements:

    a. All primitives (int, float, boolean etc.)
    b. String
    c. Class
    d. Enums
    e. Annotations
    f. Arrays of any of the above

 

9) The compiler is quite picky about default element values. Elements must either have default values or values provided by the class that uses the annotation. And none of the non-primitive type elements are allowed to take null as a value, either when declared in the source code or when defined as a default value in the annotation interface. This makes it hard to write a processor that acts on the presence or absence of an element, because every element is effectively present in every annotation declaration. You can get around this by checking for specific values, like empty strings or negative values.

 

10) Each ElementType that you specify in the @Target annotation is a restriction that tells the compiler that your annotation can only be applied to that particular type. You can specify a single value of the enum ElementType , or you can specify a comma-separated list of any combination of values. If you want to apply the annotation to any ElementType , you can leave out the @Target annotation altogether.

 

11) Annotations are especially useful when working with frameworks that require some sort of additional information to accompany your source code. After defining a Java class, the programmer must undergo the tedium of respecifying information like the name, package and so on—information that already exists in the original class. Whenever you use an external descriptor file, you end up with two separate sources of information about a class, which usually leads to code synchronization problems. This also requires that programmers working on the project must know about editing the de  script  or as well as how to write Java programs.

 

12) If you define an element on an annotation with the name value , then as long as it is the only element value specified you don’t need to use the name-value pair syntax; you can just specify the value in parentheses. This can be applied to any of the legal element types. Of course this limits you to naming your element "value".

 

13) The compiler allows as many different annotations as you like on an annotation target. Note that when using multiple annotations, you cannot use the same annotation twice.

 

14) You cannot use the extends keyword with @interfaces .

 

15) AnnotatedElement. getDeclaredAnnotations( ) returns an array of all of the defined annotations ( Annotation[] ) for a particular annotated element.

 

16) The annotation processing tool apt is Sun’s first version of a tool that aids the processing of annotations. apt is designed to be run on Java source files rather than compiled classes. By default, apt compiles the source files when it has finished processing them.

 

17) When your annotation processor creates a new source file, that file is itself checked for annotations in a new round (as it is referred to in the documentation) of processing. The apt tool will continue round after round of processing until no more source files are being created. It then compiles all of the source files.

 

18) The apt tool can easily group several annotation processors together. It allows you to specify multiple classes to be processed. You can also add listeners to receive notification of when an annotation processing round is complete.

 

19) apt works by using an AnnotationProcessorFactory to create the right kind of annotation processor for each annotation it finds. When you run apt , you specify either a factory class or a classpath where it can find the factories it needs. 

 

20) When you create an annotation processor for use with apt , you can’t use the reflection features in Java because you are working with source code, not compiled classes (However, using the non-standard -XclassesAsDecls option, you may work with annotations that are in compiled classes.) The mirror API solves this problem by allowing you to view methods, fields and types in uncompiled source code.(You must have tools.jar in your classpath.)

 

21) There are only three methods on the AnnotationProcessorFactory interface. The one which provides the processor is getProcessorFor( ), which takes a Set of annotation type declarations (the annotation definition apt tool found in the classes being run against), and the AnnotationProcessorEnvironment object, which you can pass to the processor. The other two methods, supportedAnnotationTypes( ) and supportedOptions( ) should return a Set of full class name of annotation and command line option you support. apt will only inoke the factory to generate annotation processor to process those annotations supported by the factory (AnnotationProcessor.process() will be invoked).

 

22) AnnotationProcessorEnvironment.getSpecifiedTypeDeclarations() returns all of the types (class definitions) that the apt tool is processing. getMessage() method returns a Messager object which enables you to report messages to the user. getFiler() method returns a Filer object whose createSourceFile( ) opens an ordinary output stream with the correct name for the new Java class or interface file to be generated. It allows apt to keep track of any new files that you create, so it can check them for annotations and compile them if it needs to.

 

23) TypeDeclaration.getMethods() returns all MethodDeclaration defined in the type. MethodDeclaration.getModifiers() returns all the modifiers of the method.

 

24) The following commands tells apt to use the factory class defined and process the file Multiplier.java. The -s option specifies that any new files must be created in the directory annotations:

 

apt -factory annotations.InterfaceExtractorProcessorFactory Multiplier.java -s ../annotations
 

 

25) A Visitor traverses a data structure or collection of objects, performing an operation on each one. The data structure need not be ordered, and the operation that you perform on each object will be specific to its type. This decouples the operations from the objects themselves, meaning that you can add new operations without adding methods to the class definitions. A Java class can be thought of as a collection of objects such as TypeDeclarations, FieldDeclarations, MethodDeclarations, and so on. When you use the apt tool with the Visitor pattern, you provide a Visitor class which has a method for handling each type of declaration that you visit.

 

26) TypeDeclaration.accept can take a instance of DeclarationVisitor to vist the annotations declared in the type. DeclarationVisitors.getDeclarationScanner( ) can generate one DeclarationVistor instance from two DeclarationVisitor instances : the first is used before each declaration is visited and the second is used afterwards. DeclarationVisitors.NO_OP is a DeclarationVisitor that doesn't do anything. SimpleDeclarationVisitor is a simple implementation of DeclarationVistor interface which has method to visit the annotations on different element of a type.

 

27) All the apt API discussed here have already been deprecated, please refer to : http://docs.oracle.com/javase/7/docs/technotes/guides/apt/GettingStarted.html. 

 

28) The apt tool and its associated API contained in the package com.sun.mirror have been deprecated since JDK 7 and has been removed in the JDK 8 release. Use the options available in the javac tool and the APIs contained in the packages javax.annotation.processing and javax.lang.model to process annotations.

 

  • 大小: 122.2 KB
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics