Android基础知识-注解Annotation

转载:https://www.jianshu.com/p/65c4af2ce8f0

一、Java注解相关Annotation 转载

注解也会被编译成class文件

注解的声明
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(value = {TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE, ANNOTATION_TYPE,PACKAGE})
public @interface HelloAnnotation {
}

在定义注解时需要一些元注解,如@Retention和@Target。

五种元注解
元注解说明取值
@Target表示该注解可以用在什么地方ElementType 的枚举值:
TYPE:用于类、接口(包括注解类型)或enum声明
FIELD:用于字段声明,包括enum实例
METHOD:用于方法声明
PARAMETER:用于参数声明
CONSTRUCTOR:用于构造函数声明
LOCAL_VARIABLE:用于局部变量声明
ANNOTATION_TYPE:用于注解可用于注解声明(应用于另一个注解上)
PACKAGE:用于包声明
TYPE_PARAMETER:用于类上泛型参数的说明(JDK1.8加入)
TYPE_USE:用于任何类型的声明(JDK1.8加入)
@Retention表示该注解在什么级别下被保存
注解未定义 Retention值时,默认值是 CLASS级别
RetentionPolicy 的枚举值:
SOURCE只会保留在源码里,编译后不存在该注解信息
CLASS保留至 class文件,在执行的时候,不会被加载到虚拟机中
RUNTIMEVM 在运行期间保留注解,可以通过反射机制读取到注解的信息
@Documented表示是否将注解包含在 JavaDoc 中跳转到项目的目录,打开命令行,
执行
javadoc -encoding utf-8 -charset utf-8 -package com.test.annotation 命令
生成文档
@Inherited表示允许子类继承父类中的注解父类使用该注解后,子类也可以获得该注解
@Repeatable看下面详细说明

注解的属性

属性说明
支持的属性类型所有的基本类型(int、float、boolean)等
String
Class
enum
Annotaion
以及以上类型的数组
注解添加属性基本语法是: 类型 属性名()
@interface WorldAnnotation {
//基本类型及其数组类型
      int intAttr();
}
指定属性默认值语法为:属性 属性名() default 默认值;
@interface WorldAnnotation {
//基本类型及其数组类型
      int  intAttr()  default  -1;
}
注解默认值限制1、注解中的元素,必须 要么有默认值,要么在使用注解时提供元素的值
2、非基本类型的元素,不能以 null 作为其值
value 属性一个注解中只有一个名称为 value 的属性
或者,注解有多个属性,只有 value 属性没有默认值
填写注解值时,可以省略掉 “value=” 部分
@WorldAnnotation(“hello”)
@WorldAnnotation(“value = hello”) //可以省略调 “value=”

1、@Target 元注解
表示该注解可以用在什么地方。

1)使用方式

// TYPE:用于类、接口(`包括注解类型`)或enum声明
@HelloAnnotation
class AnnotationDemo {

	//CONSTRUCTOR:用于构造函数声明
	@HelloAnnotation
	public AnnotationDemo(String name) {
		this.name = name;
	}

	//FIELD:用于字段声明,包括enum实例
	@HelloAnnotation
	private String name;

	//METHOD:用于方法声明
	@HelloAnnotation
	public void sayHello() {
		System.out.println("hello every one");
	}

	//PARAMETER:用于参数声明
	public void saySometing(@HelloAnnotation String text) { }
	
	//LOCAL_VARIABLE:用于局部变量声明
	public int add(int a, int b) {
		@HelloAnnotation int total = 0;
		return a + b;
	}
	
	//ANNOTATION_TYPE:用于注解可也用于注解声明(应用于另一个注解上)
	@HelloAnnotation
	@interface AnnotationTwo {

	}
}

2)对包进行注解修饰,需要在当前包下创建【package-info.java 文件】

  • package-info.java 文件的作用:
    i. 为标注在包上 Annotation提供便利
    ii. 声明包的 私有类和常量
    iii.提供包的整体注释说明
    (如果是项目是分“包”开发,
    也就是说一个包实现一个业务逻辑或功能点、或模块、或组件,
    则需要对一个包有很好的说明,
    说明这个包是干啥的,有啥作用,版本变迁,特别说明等等)

    /**
     * 主要是为了测试包的注解
     */
    @HelloAnnotation
    package com.test.annotation;
    

3)JDK1.8中新增的 TYPE_PARAMETER、TYPE_USE

  • TYPE_PARAMETER 用于 修饰类上的【泛型参数】

    @Retention(RetentionPolicy.RUNTIME)
    @Target(value = {TYPE_PARAMETER})
    public @interface HelloAnnotation {
    }
    class A<@HelloAnnotation T> {}
    
  • TYPE_USE 用于任何类型声明(也包含修饰类上的泛型参数)

    @Retention(RetentionPolicy.RUNTIME)
    @Target(value = {TYPE_USE})
    public @interface HelloAnnotation {
    }
    
    @HelloAnnotation
    class AnnotationDemo<@HelloAnnotation T> {
    
        @HelloAnnotation
        public AnnotationDemo(String name) {
      	  this.name = name;
        }
    
        @HelloAnnotation
        private String name;
    
        public void saySometing(@HelloAnnotation String text) { }
    
        public int add(int a, int b) {
      	  @HelloAnnotation int total = 0;
      	  return a + b;
        }
    
        @HelloAnnotation
        @interface AnnotationTwo { }
    }		
    

2)@Retention 元注解
表示该注解在什么级别下被保存。
当注解未定义Retention值时,默认值是CLASS级别

3)@Documented 元注解
表示是否将注解包含在 JavaDoc 中

@Documented
@Retention(RetentionPolicy.RUNTIME)
public @interface HelloAnnotation {
}

@HelloAnnotation
class AnnotationDemo {

	@HelloAnnotation
	public AnnotationDemo(String name) {
		this.name = name;
	}
	
	@HelloAnnotation
	private String name;

	public void saySometing(@HelloAnnotation String text) { }

	public int add(int a, int b) {
		@HelloAnnotation int total = 0;
		return a + b;
	}
}

跳转到项目的目录,打开命令行,
执行 javadoc -encoding utf-8 -charset utf-8 -package com.test.annotation 命令
(这里是所有的文件都是放在annotaton包下的,你可以根据你自己的包名为该包下的所有.java文件生成Doc文档)
如果为 该注解 指定了 @Documented 元注解,生成的文档中,会包含 该注解。

4)@Inherited 元注解
表示允许子类继承父类中的注解。

@Target(ElementType.TYPE)
@Inherited // @Hello 注解允许子类继承
@Retention(RetentionPolicy.RUNTIME)
@interface Hello {
}

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface World {
}

@Hello
class Person { //父类中使用了 @Hello 注解
}

// 子类虽然没有直接使用 @Hello 注解,但是会继承父类使用的 @Hello 注解
@World
class Man extends Person {
	public static void main(String[] args) {
		Annotation[] annotations = Man.class.getAnnotations();
		for (Annotation annotation : annotations) {
			System.out.println(annotation.annotationType());
		}
	}
}

//输出结果
interface annotation.Hello
interface annotation.World

5)@Repeatable 元注解 (JDK1.8 新增)
表示,可以在同一个地方多次使用同一种注解类型。

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Repeatable(FilterPaths.class)//添加@Repeatable元注解,注意其中的值【FilterPaths.class】
public @interface FilterPath {
	String value();
}

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface FilterPaths {
	FilterPath[] value();//注解其中的【数组类型为 FilterPath】
}

//现在可以在同一个地方使用同一注解啦~
@FilterPath("hello/java")
@FilterPath("hello/android")
class FileOperate {
}

@Repeatable元注解使用 注意事项
为了处理@Repeatble注解,
JDK1.8 在 AnnotatedElement 接口中提供了 getAnnotationsByType 与 getDeclaredAnnotationsByType 方法,

(注意:如果我们采用传统的方法,也就是 getAnnotation(Class<A> annotationClass)方法来获取声明的注解,
我们需要传入注解容器的class,而不是需要重复的注解的class)。

public staticvoid main(String[] args) {
	//从该类上获取 FilterPath 注解信息
	FilterPath filterPath = FileOperate.class.getAnnotation(FilterPath.class);
	System.out.println(filterPath);//输出结果 null

	//从该类上获取 FilterPaths 注解信息
	FilterPaths filterPaths = FileOperate.class.getAnnotation(FilterPaths.class);
	
	//输出结果 @annotation.FilterPaths(value=[@annotation.FilterPath(value=hello/java), @annotation.FilterPath(value=hello/android)])
	System.out.println(filterPaths);
	
	for (FilterPath path : filterPaths.value()) {
		/*输出结果
				hello/java
				hello/android
		 */
		System.out.println(path.value());
	}

	//通过 getAnnotationsByType
	FilterPath[] annotationsByType = FileOperate.class.getAnnotationsByType(FilterPath.class);
	if (annotationsByType != null) {
		for (FilterPath path : annotationsByType) {
			/*输出结果
					hello/java
					hello/android
			 */
			System.out.println(path.value());
		}
	}

	//通过 getDeclaredAnnotationsByType
	FilterPath[] declaredAnnotationsByType = FileOperate.class.getDeclaredAnnotationsByType(FilterPath.class);
	if (declaredAnnotationsByType != null) {
		for (FilterPath path : declaredAnnotationsByType) {
			/*输出结果
					hello/java
					hello/android
			 */
			System.out.println(path.value());
		}
	}
}

不能通过 getAnnotation(FilterPath.class)获取注解(获得的注解为null)
需要通过 getAnnotation(FilterPaths.class)来获取)

getAnnotationsByType(FilterPath.class)、getDeclaredAnnotationsByType(FilterPath.class)就能获取到正确的值。

getAnnotationsByType 与 getDeclaredAnnotationsByType 方法的区别:
子类调用 getAnnotationsByType 方法且该子类的父类中声明了用@Inherited修饰的注解,那么可以获得父类中的注解。
getDeclaredAnnotationsByType 是获取不到父类中声明的注解的。

3、注解支持属性类型(注解元素中可以定义的属性)

1)注解支持的属性类型
所有的基本类型(int、float、boolean)等
String
Class
enum
Annotaion
以及以上类型的数组

2)注解添加属性
基本语法是: 类型 属性名();

//声明枚举
enum Week {
	MONDAY,
	TUESDAY,
	WEDNESDAY,
	THURSDAY,
	FRIDAY,
	SATURDAY,
	SUNDAY
}

@Target(ElementType.TYPE_USE)
@Retention(RetentionPolicy.RUNTIME)
public @interface HelloAnnotation {
	String text();
}

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface WorldAnnotation {

	//基本类型及其数组类型
	int intAttr();
	float floatAttr() ;
	boolean booleanAttr() ;

	int[] intArray() ;
	float[] floatArray() ;
	boolean[] booleanArry() ;

	//String类型及其数组类型
	String stringAttr() ;
	String[] stringArray();

	//Class类型及其数组类型
	Class classAttr() ;
	Class[] classArray();

	//enum类型及其数组类型
	Week day() ;
	Week[] week();

	//注解嵌套
	HelloAnnotation HelloAnnotation();
}

3)为属性 指定缺省值(默认值)
语法为:属性 属性名() default 默认值;

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface WorldAnnotation {

	//基本类型及其数组类型
	int intAttr() default -1;

	float floatAttr() default -1f;

	boolean booleanAttr() default false;

	int[] intArray() default {1, 2, 3};

	float[] floatArray() default {1f, 2f, 3f};

	boolean[] booleanArry() default {true, false, true};

	//String类型及其数组类型
	String stringAttr() default "";

	String[] stringArray();

	//Class类型及其数组类型
	Class classAttr() default Class.class;

	Class[] classArray();

	//enum类型
	Week day() default Week.MONDAY;

	//enum数组类型
	Week[] week() default {Week.MONDAY,Week.THURSDAY};

	//注解嵌套
	HelloAnnotation HelloAnnotation() default @HelloAnnotation(text = "word");
}

4)注解默认值限制

注解中的元素,必须【要么有默认值,要么在使用注解时提供元素的值
非基本类型的元素,不能以 null 作为其值

这个约束使得处理器很难发现一个元素的存在和缺失的状态,
因为在每个注解的声明中,所有的元素都存在,并且都具有相应的值。
为了绕开这个约束,我们只能定义一些特殊的值,例如空字符串或负数,以此表示某个元素不存在。

5)value 属性
如果一个注解中有一个名称为value的属性,且你只想设置value属性
(即其他属性都采用默认值或者你只有一个value属性),那么可以省略掉“value=”部分

/* 第一种情况,只有一个vaule属性,
	那么你在使用时候可以直接 @WorldAnnotation("hello")
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface WorldAnnotation {
	String value();
}

//第二种情况,有多个属性但是其属性都有默认值,
//你只使用value属性,那么你在使用时候可以直接@WorldAnnotation("hello")
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface WorldAnnotation {
	int intAttr() default -1;
	float floatAttr() default -1f;
	String value();
}
4、注解与反射机制

1)注解与反射机制的关系
反射机制:在运行状态中,动态获取信息、动态调用对象方法的功能
生命周期为 @Retention(RetentionPolicy.RUNTIME) 的注解
编译时会添加到class文件中。
JVM加载的 Class对象,也会带有注解信息。

通过Class对象中的 Constructor、Field、Method 等类,就能获取其上声明的注解信息。
编译后,其实注解最终会继承 Annotation接口。
注解最终会以 java.lang.annotation.Annotation对象 的形式在Class对象中进行展示或存储。

2)注解的处理

Constructor、Field、Method、Class、Package类都实现了 AnnotatedElement接口

注解处理交给了 AnnotatedElement 接口 来实现

public interface AnnotatedElement {
	
	//返回元素上指定类型的注解,如果无,则返回为null
	<T extends Annotation> T getAnnotation(Class<T> clazz);

	//返回元素上存在的所有注解,包括从父类继承的
	Annotation[] getAnnotations();

	/**返回元素上指定的类型的注解数组,包括父类的注解,
	 * 如果无,返回长度为0的数组,
	 * 该方法与 getAnnotation(Class<T> clazz) 的主要区别是:
	 * 		该方法【可以检查注解是不是重复的。】
	 */
	default <T extends Annotation> T[] getAnnotationsByType(Class<T> clazz) {
		return AnnotatedElements.getDirectOrIndirectAnnotationsByType(this, clazz);
	}

	//返回该元素上【指定类型】的所有注解,【不包括父类的注解】,如果无,返回长度为0的数组
	default <T extends Annotation> T getDeclaredAnnotation(Class<T> clazz) {
		Objects.requireNonNull(clazz);
		for (Annotation annotation : getDeclaredAnnotations()) {
			if (clazz.equals(annotation.annotationType())) {
				return clazz.cast(annotation);
			}
		}
		return null;
	}

	//同getAnnotationsByType(Class<T> clazz)方法类似,只是获取的注解中【不包括父类的注解】
	default <T extends Annotation> T[] getDeclaredAnnotationsByType(Class<T> clazz) {
		return AnnotatedElements.getDirectOrIndirectAnnotationsByType(this, clazz);
	}

	//返回该元素上的所有的注解,不包括父类的注解,如果无,返回长度为0的数组
	Annotation[] getDeclaredAnnotations();
}

getAnnotationsByType(Class clazz)与 getDeclaredAnnotationsByType(Class clazz)方法
是jdk 1.8之后提供的接口默认实现方法。支持 @Repeatable 元注解。
其他方法是不支持 @Repeatable 的。

3)注解的使用

[1]定义注解

//什么人
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface Who {
	String name();
	int age();
}

//在哪里
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface Where {
	String country();
	String province();
	String city();
}

//做了什么事
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@interface DoSomething {
	String value();
}

[2]使用定义的注解

@Who(name = "AndyJenifer", age = 18)
class Person {

	@Where(country = "中国", province = "四川", city = "成都")
	private String where;

	@DoSomething("写博客")
	public void doSomething() {
	}
	
	public static void main(String[] args) {
		Class<Person> personClass = Person.class;
		StringBuffer sb = new StringBuffer();

		//获取类上的注解
		Who who = personClass.getAnnotation(Who.class);
		sb.append(who.name());
		sb.append(who.age());

		//获取字段上的注解
		Field[] fields = personClass.getDeclaredFields();
		for (Field field : fields) {
			Annotation[] annotations = field.getAnnotations();
			if (annotations.length > 0 && annotations[0] instanceof Where) {
				Where where = (Where) annotations[0];
				sb.append(where.country());
				sb.append(where.province());
				sb.append(where.city());
			}
		}

		//获取方法上的注解
		Method[] methods = personClass.getMethods();
		for (Method method : methods) {
			Annotation[] annotations = method.getAnnotations();
			if (annotations.length > 0 && annotations[0] instanceof DoSomething) {
				DoSomething doSomething = (DoSomething) annotations[0];
				sb.append(doSomething.value());
			}
		}
		System.out.println(sb.toString());
	}
}

getXXXX():获得 某个类、包括父类 的所有的公共(public)的元素(如Constructor、Field、Method、Package),
包括父类声明的。

getDeclaredXXXX():获得 某个类、不包括父类 的所有声明的元素(如Constructor、Field、Method、Package),
包括 public、private、proteced,但是不包括父类声明的。

转载自:
Android 注解系列之Annotation(二)
Android 注解系列之APT工具(三)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值