0%

Annotation

太久没用的知识点总是忘,昨晚又翻了一遍,整理一下。

注解简介

Java SE5 内置3种标准注解:@Override、@Deprecated、@SupportWarnings 名字能分辨出其注解作用。

4种元注解:

  • @Target 表示修饰注解可以的地方,可能的ElementType包括 CONSTRUCTOR(注解构造器)、FIELD(注解 域,包括enum实例)、LOCAL_VARIABLE(注解局部变量)、METHOD(注解方法)、PACKAGE(注解包)、PARAMETER(注解参数)、TYPE(注解类,接口或enum声明)、ANNOTATION_TYPE(注解 注解)、TYPE_PARAMETER(1.8加入,注解方法参数)、TYPE_USE(1.8加入 注解使用类型)
  • @Retention 表示可以在什么级别保存该注解信息,可选参数RetentionPolicy包括 SOURCE(注解讲被编译器丢弃)、CLASS(注解在class中可用,但会被vm丢弃)、RUNTIME(vm运行时也可以保留注解,可以通过反射机制读取注解信息)
  • @Documented 将注解包含到javadoc中
  • @Inherited 允许子类继承父类中的注解

示例

编写一个方法注解

1
2
3
4
5
6
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface UseCase {
public int id();
public String description() default "no description";
}

使用注解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class Annotation {

@UseCase(id= 14,description = "print someThing!")
public static void print(String message) {
System.out.println("message : "+message);
}

@UseCase(id= 18)
public static void password(String message) {
System.out.println("message : "+message);
}
@UseCase(id= 10,description = "input your name!")
public static void name(String message) {
System.out.println("message : "+message);
}
}

注解处理器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class UseCaseHandler {
public static void trackUseCase(List<Integer> useCases, Class<?> c1) {
for (Method m : c1.getDeclaredMethods()) {
UseCase uc = m.getAnnotation(UseCase.class);
if (uc != null) {
System.out.println("Find useCase : "+uc.id()+ " description: "+uc.description());
useCases.remove(new Integer(uc.id()));
}
}
for (int i : useCases) {
System.out.println("missing --> "+i);
}
}

public static void main(String[] args) {
List<Integer> useCases = new ArrayList<>();
Collections.addAll(useCases,10,18,14,20);
trackUseCase(useCases,Annotation.class);
}
}

输出:

1
2
3
4
Find useCase : 10 description: input your name!
Find useCase : 14 description: print someThing!
Find useCase : 18 description: no description
missing --> 20

以上就是一个简单的示例,注解套路基本同上。

一些知识点

注解元素

注解元素可用类型包括

  • 所有的基本类型
  • String
  • Class
  • enum
  • 以上类型的数组
    注解元素必须有默认值,也就是default值。为了表示一个空,可以定义一个注解来表示空注解
    1
    2
    3
    4
    5
    6
    @Target(ElementType.ANNOTATION_TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface DefaultNull {
    int value() default -1;
    String description() default "";
    }

注解嵌套。

1
2
3
4
5
6
7
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@DefaultNull //出现在这里的注解一般是起限定作用,类似一个大类中的小类标记。比如狗,分泰迪,吉娃娃,田园犬,可以用一个枚举注解在这里区分,不用再写很多元素和@interface。
@....//可以有多个自定义注解出现。
public @interface Simple {
DefaultNull getDefault() default @DefaultNull(value = 2);//修改默认值
}

这里用 DefaultNull相当于一个Tag,用以表示一个默认值,可以根据反射取得Simple的所以注解,如果发现该注解则。。。。

最后写一个注解注入的简单例子。

AnimalType

枚举类,划分内部注解范畴

1
2
3
4
5
6
7
8
9
10
public enum AnimalType {
DOG("DOG"),CAT("CAT");
private String name;
AnimalType(String name) {
this.name = name;
}
public String getName() {
return name();
}
}

POJO类

1
2
3
4
5
6
7
8
9
public class Dog {
public String action;
public Dog(String action) {
this.action = action;
}
}
//实际上它们是分开的
public class Cat {
}

CategoryAnnotation

基础种类注解

1
2
3
4
5
6
@Target(ElementType.ANNOTATION_TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface CategoryAnnotation {
AnimalType animalType();
String name();
}

注解类

定义吉娃娃和泰迪注解,它们同时也被 CategoryAnnotation注解,CategoryAnnotation的存在,不需要再在这两个注解类的内部声明新元素。

1
2
3
4
5
6
7
8
9
10
11
12
13
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@CategoryAnnotation(animalType = AnimalType.DOG,name = "Ji-wa-wa")
public @interface JiwawaAnnotation {
}

//实际上它们是分开的

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@CategoryAnnotation(animalType = AnimalType.DOG, name ="Tai-di" )
public @interface TaidiAnnotation {
}

inject类,注入类。实现注解注入

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
public class InjectUtil {
public static void inject(Test cl) {
Field[] fields = cl.getClass().getFields();
for (Field field : fields) {
Annotation[] annotations = field.getAnnotations();
for (Annotation annotation : annotations) {
Class<?> annotationType = annotation.annotationType(); //返回方法上面使用的注解类
CategoryAnnotation categoryAnnotation = annotationType.getAnnotation(CategoryAnnotation.class);
if (categoryAnnotation != null) {
String name = categoryAnnotation.name();
AnimalType type = categoryAnnotation.animalType();
try {
if (type == AnimalType.DOG) {
if (name.equals("Ji-wa-wa")) {
field.set(cl, new Dog("ji wa wa start sa huan!"));//没错就是 吉娃娃开始撒欢
}else {
field.set(cl,new Dog("tai di start run!")); //泰迪开始跑
} else if (type == AnimalType.CAT) {
field.set(cl,new Cat());
}
} catch (Exception e) {
e.printStackTrace();
}
} else {
System.out.println("no categoryAnnotation!");
}
}
}
}
}

最后是Test类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class Test {
{
InjectUtil.inject(this);
}

@TaidiAnnotation
public Dog dog1;
@JiwawaAnnotation
public Dog dog2;

public void liuGou() {
System.out.println("dog1.action : "+dog1.action);
System.out.println("dog2.action : "+dog2.action);
}

public static void main(String[] args) {
Test test = new Test();
test.liuGou();
}
}

OutPut

1
2
dog1.action : tai di start run!
dog2.action : ji wa wa start sa huan!

示例总结

这里实现了依赖注入,但是injectUtil可以抽离出来,讲类需要的依赖进行Module封装,再通过contract连接就是dagger的框架了。
上例主要演示注解在反射机制下的运用,要使用该机制 @Retention(RetentionPolicy.RUNTIME) 是必须的。

以上示例地址