Java注解

0.562字数 1590阅读 261

什么是注解

注解对于开发人员来讲既熟悉又陌生,熟悉是因为只要你是做开发,都会用到注解(常见的@Override);陌生是因为即使不使用注解也照常能够进行开发;注解不是必须的,但了解注解有助于我们深入理解某些第三方框架(比如Android Support Annotations、JUnit、xUtils、ActiveAndroid等),提高工作效率。

常见注解

API
Android开发过程中使用到的注解主要来自如下几个地方:

  • Android SDK:在包android.annotation下;
  • Android Annotation Support包:在包android.support.annotation下;
  • JDK:在包java.lang下;

第三方框架中的自定义注解;

最常见注解

  • @Override
    属于标记注解,不需要设置属性值;只能添加在方法的前面,用于标记该方法是复写的父类中的某个方法,如果在父类没有的方法前面加上@Override注解,编译器会报错:
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}
  • @Deprecated
    属于标记注解,不需要设置属性值;可以对构造方法、变量、方法、包、参数标记,告知用户和编译器被标记的内容已不建议被使用,如果被使用,编译器会报警告,但不会报错,程序也能正常运行:
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.CONSTRUCTOR, ElementType.FIELD, ElementType.LOCAL_VARIABLE, ElementType.METHOD, ElementType.PACKAGE, ElementType.PARAMETER, ElementType.TYPE})
public @interface Deprecated {
}
  • @SuppressWarnings
    可以对构造方法、变量、方法、包、参数标记,用于告知编译器忽略指定的警告,不用再编译完成后出现警告信息:
@Target({ElementType.TYPE, ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.CONSTRUCTOR, ElementType.LOCAL_VARIABLE})
@Retention(RetentionPolicy.SOURCE)
public @interface SuppressWarnings {
    String[] value();
}
  • @TargetApi
    可以对接口、方法、构造方法标记,如果在应用中指定minSdkVersion为8,但有地方需要使用API 11中的方法,为了避免编译器报错,在调用API11中方法的接口、方法或者构造方法前面加上@Target(11),这样该方法就可以使用<=11的API接口了。虽然这样能够避免编译器报错,但在运行时需要注意,不能在API低于11的设备中使用该方法,否则会crash(可以获取程序运行设备的API版本来判断是否调用该方法):
@Target({TYPE, METHOD, CONSTRUCTOR})
@Retention(RetentionPolicy.CLASS)
public @interface TargetApi {
    /**
     * This sets the target api level for the type..
     */
    int value();
}
  • @SuppressLint
    和@Target的功能差不多,但使用范围更广,主要用于避免在lint检查时报错:
@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
@Retention(RetentionPolicy.CLASS)
public @interface SuppressLint {
    /**
     * The set of warnings (identified by the lint issue id) that should be
     * ignored by lint. It is not an error to specify an unrecognized name.
     */
    String[] value();
}

自定义注解

通过阅读注解类的源码可以发现,任何一个注解类都有如下特征:

注解类会被@interface标记;

注解类的顶部会被@Documented、@Retention、@Target、@Inherited这四个注解标记(@Documented、@Inherited可选,@Retention、@Target必须要有);

@UiThread源码:

@Documented
@Retention(CLASS)
@Target({METHOD,CONSTRUCTOR,TYPE})
public @interface UiThread {
}

元注解

上文提到的四个注解:@Documented、@Retention、@Target、@Inherited就是元注解,它们的作用是负责注解其它注解,主要是描述注解的一些属性,任何注解都离不开元注解(包括元注解自身,通过元注解可以自定义注解),元注解的用户是JDK,JDK已经帮助我们实现了这四个注解的逻辑。这四个注解在JDK的java.lang.annotation包中。对每个元注解的详细说明如下:

@Target:

作用:用于描述注解的使用范围,即被描述的注解可以用在什么地方;

取值:

  • CONSTRUCTOR:构造器;
  • FIELD:实例;
  • LOCAL_VARIABLE:局部变量;
  • METHOD:方法;
  • PACKAGE:包;
  • PARAMETER:参数;
  • TYPE:类、接口(包括注解类型) 或enum声明。

示例:

/***
 *
 * 实体注解接口
 */
@Target(value = {ElementType.TYPE})
@Retention(value = RetentionPolicy.RUNTIME)
public @interface Entity {
    /***
     * 实体默认firstLevelCache属性为false
     * @return boolean
     */
    boolean firstLevelCache() default false;
    /***
     * 实体默认secondLevelCache属性为false
     * @return boolean
     */
    boolean secondLevelCache() default true;
    /***
     * 表名默认为空
     * @return String
     */
    String tableName() default "";
    /***
     * 默认以""分割注解
     */
    String split() default "";
}

@Retention:

作用:表示需要在什么级别保存该注解信息,用于描述注解的生命周期,即被描述的注解在什么范围内有效;

取值:

  • SOURCE:在源文件中有效,即源文件保留;
  • CLASS:在class文件中有效,即class保留;
  • RUNTIME:在运行时有效,即运行时保留;

示例:

/***
 * 字段注解接口
 */
@Target(value = {ElementType.FIELD})//注解可以被添加在实例上
@Retention(value = RetentionPolicy.RUNTIME)//注解保存在JVM运行时刻,能够在运行时刻通过反射API来获取到注解的信息
public @interface Column {
    String name();//注解的name属性
}

@Documented:

作用:用于描述其它类型的annotation应该被作为被标注的程序成员的公共API,因此可以被例如javadoc此类的工具文档化。

取值:它属于标记注解,没有成员;

示例:

@Documented
@Retention(CLASS)
@Target({METHOD,CONSTRUCTOR,TYPE})
public @interface UiThread {
}

@Inherited:

作用:用于描述某个被标注的类型是可被继承的。如果一个使用了@Inherited修饰的annotation类型被用于一个class,则这个annotation将被用于该class的子类。

取值:它属于标记注解,没有成员;

示例:

/**  
 * @author wangsheng 
 **/  
@Inherited  
public @interface Greeting {  
    public enum FontColor{ BULE,RED,GREEN};  
    String name();  
    FontColor fontColor() default FontColor.GREEN;  
} 

如何自定义注解

使用@interface自定义注解时,自动继承了java.lang.annotation.Annotation接口,由编译程序自动完成其他细节。在定义注解时,不能继承其他的注解或接口。@interface用来声明一个注解,其中的每一个方法实际上是声明了一个配置参数。方法的名称就是参数的名称,返回值类型就是参数的类型(返回值类型只能是基本类型、Class、String、enum)。可以通过default来声明参数的默认值。

自定义注解格式:

元注解
public @interface 注解名{
    定义体;
}

注解参数可支持的数据类型:
1、所有基本数据类型(int,float,boolean,byte,double,char,long,short);
2、String类型;
3、Class类型;
4、enum类型;
5、Annotation类型;
6、以上所有类型的数组。

特别说明:

1、注解类中的方法只能用public或者默认这两个访问权修饰,不写public就是默认,eg:

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface FruitColor {
    public enum Color{ BULE,RED,GREEN};
    Color fruitColor() default Color.GREEN;
}

2、如果注解类中只有一个成员,最好把方法名设置为"value",比如:

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface FruitName {
    String value() default "";
}

3、注解元素必须有确定的值,要么在定义注解的默认值中指定,要么在使用注解时指定,非基本类型的注解元素的值不可为null。因此, 使用空字符串或0作为默认值是一种常用的做法。

实例演示:
ToDo.java:注解类

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@interface Todo {
    public enum Priority {LOW, MEDIUM, HIGH}
    public enum Status {STARTED, NOT_STARTED}    
    String author() default "Yash";
    Priority priority() default Priority.LOW;
    Status status() default Status.NOT_STARTED;
}

BusinessLogic:使用注解的类

public class BusinessLogic {
    public BusinessLogic() {
        super();
    }

    public void compltedMethod() {
        System.out.println("This method is complete");
    }    

    @Todo(priority = Todo.Priority.HIGH)
    public void notYetStartedMethod() {
        // No Code Written yet
    }

    @Todo(priority = Todo.Priority.MEDIUM, author = "Uday", status = Todo.Status.STARTED)
    public void incompleteMethod1() {
        //Some business logic is written
        //But its not complete yet
    }

    @Todo(priority = Todo.Priority.LOW, status = Todo.Status.STARTED )
    public void incompleteMethod2() {
        //Some business logic is written
        //But its not complete yet
    }
}

TodoReport.java:解析注解信息

public class TodoReport {
    public TodoReport() {
        super();
    }

    public static void main(String[] args) {
        getTodoReportForBusinessLogic();
    }

    /**
     * 解析使用注解的类,获取通过注解设置的属性
     */
    private static void getTodoReportForBusinessLogic() {
        Class businessLogicClass = BusinessLogic.class;
        for(Method method : businessLogicClass.getMethods()) {
            Todo todoAnnotation = (Todo)method.getAnnotation(Todo.class);
            if(todoAnnotation != null) {
                System.out.println(" Method Name : " + method.getName());
                System.out.println(" Author : " + todoAnnotation.author());
                System.out.println(" Priority : " + todoAnnotation.priority());
                System.out.println(" Status : " + todoAnnotation.status());
                System.out.println(" --------------------------- ");
            }
        }
    }
}

参考

【1】深入浅出Java注解

推荐阅读更多精彩内容