目录
APT注解处理器Annotation Processing Tool
Android注解小应用(通过注解findViewById)
简述
Java 注解(Annotation)又称 Java 标注,是 JDK5.0 引入的一种注释机制。 注解是元数据的一种形式,提供有关于程序但不属于程序本身的数据。注解对它们注解的代码的操作没有直接影响
然后说注解本质的操作机理 通过反射获取注解信息 根据注解信息作出处理
分类
JDK提供的注解 第三方框架的注解 自定义注解
JDK提供的注解
Java 定义了一套注解,目前共有 10 个。有三个是在Java7后加入的。此处仅统计至Java8。下面仅介绍注解的基本意义,具体常用注解的使用方式请往后看。
@Override
检查该方法是否是重写方法。如果发现其父类,或者是引用的接口中并没有该方法时,会报编译错误。
@Deprecated
标记过时方法。如果使用该方法,会报编译警告。
@SuppressWarnings
指示编译器去忽略注解中声明的警告。
以下四个为元注解 能够标记注解的注解就是元注解 具体的使用请看后续元注解的使用
@Retention
标识这个注解怎么保存,是只在代码中,还是编入class文件中,或者是在运行时可以通过反射访问。
@Documented
标记这些注解是否包含在用户文档中。
@Target
标记这个注解应该是哪种 Java 成员。
@Inherited
标记这个注解是继承于哪个注解类(默认 注解并没有继承于任何子类)。
Java7后加入
@SafeVarargs
忽略任何使用参数为泛型变量的方法或构造函数调用产生的警告。
@FunctionalInterface
Java 8 开始支持,标识一个匿名函数或函数式接口。
@Repeatable
Java 8 开始支持,标识某注解可以在同一个声明上使用多次。
自定义注解
注解声明
Java中所有的注解都默认实现Annotation接口
下面就是注解的关键字 比如类的关键字是class 注解的关键字是@interface
@interface
这就是一个注解类
public @interface PrintHello {
}
元注解
上面已经介绍过了 元注解就是可注解注解的注解类 就是给注解类用的注解 呃呃呃 好像有些拗口
常用的元注解有两种
@Target 用于表示注解可应用的元素类型
ElementType.TYPE 意味着,它能标注"类、接口(包括注释类型)或枚举声明"。
ElementType.FIELD 意味着,它能标注"字段声明"。
ElementType.METHOD 意味着,它能标注"方法"。
ElementType.PARAMETER 意味着,它能标注"参数"。
ElementType.CONSTRUCTOR 意味着,它能标注"构造方法"。
ElementType.LOCAL_VARIABLE 意味着,它能标注"局部变量"。
ElementType.ANNOTATION_TYPE 意味着,它能标注"注解"。
ElementType.PACKAGE 意味着,它能标注"包声明"。
那么被ElementType.TYPE标记的注解是不是就是元注解呢?是的!
@Target(ElementType.TYPE)
@Retention 用于指定注解的存储方式(保留级别)
RetentionPolicy.SOURCE 标记的注解仅保留在源级别中,并被编译器忽略
RetentionPolicy.CLASS 标记的注解在编译时由编译器保留,但 Java 虚拟机(JVM)会忽略
RetentionPolicy.RUNTIME 标记的注解由 JVM 保留,因此运行时环境可以使用它
@Target(ElementType.TYPE) // 注解级别 任意元素
@Retention(RetentionPolicy.CLASS) // 保留级别 保留至编译器 即.class中含有注解信息
public @interface PrintHello {
}
声明注解成员
定义的成员变量只能是String、数组、Class、枚举、注解等类型
@Target({ElementType.TYPE,ElementType.FIELD,ElementType.METHOD}) // 可以注解类、属性、方法
@Retention(RetentionPolicy.RUNTIME) // 使用反射获取标记信息 保留至运行时
public @interface PrintHello {
String value(); // value 在仅有一个的情况下可以不需要指明
String name() default "猜猜我是谁"; // 可以设置默认值
}
给打印方法做个标记
@PrintHello(value = "简单使用下" , name = "我叫我不猜")
private void print(){
}
通过反射获取注解信息
public static void main(String[] args) {
Class clazz = Test.class;
try {
// 取得方法信息
Method method = clazz.getDeclaredMethod("print");
// 获取方法上的注解标记
PrintHello annotation = method.getAnnotation(PrintHello.class);
String value = annotation.value();
String name = annotation.name();
System.out.println(value+name);
} catch (Exception e) {
e.printStackTrace();
}
}
后面的案例主要以Android用例为主 仅了解注解无需向后阅读
APT注解处理器Annotation Processing Tool
注解处理器需要单独创建一个 Java Library 子模块来存放,我们创建一个名为 complier 的子模块。
然后在main下创建resources目录 继续创建META-INF.services目录
创建javax.annotation.processing.Processor文件 内容是注解处理器的全类名
比如 com.example.complier.PrintProcessor
目录如下
注解处理程序如下
package com.example.complier;
import java.util.Set;
import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.Messager;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedAnnotationTypes;
import javax.lang.model.element.TypeElement;
import javax.tools.Diagnostic;
// SupportedAnnotationTypes 指定注解处理器仅处理PrintHello
// Javac编译过程中调起 执行注解处理程序
@SupportedAnnotationTypes("com.example.annotationdemo.annotation.PrintHello")
public class PrintProcessor extends AbstractProcessor {
@Override
public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {
Messager messager = processingEnv.getMessager();
messager.printMessage(Diagnostic.Kind.NOTE,"注解处理程序");
// 想干啥干啥
// 多数场景是自动生成辅助类
return false;
}
}
比如上面代码就是打印了注解处理程序 绝大多数情况下注解处理程序用于生成辅助类
Android中的语法检查
Android包提供的一个语法检查元注解 @IntDef
新建一个日期检查注解WeekDay
package com.example.annotationdemo.annotation;
import androidx.annotation.IntDef;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@IntDef({1,2,3,4,5,6,7})
@Target({ElementType.FIELD,ElementType.PARAMETER})
@Retention(RetentionPolicy.SOURCE)
public @interface WeekDay {
}
比如给参数加个注解 那么在穿参时 非1234567外的整数会提示警告
private void censor(@WeekDay int weekDay){
}
根据不同保留级别的应用场景
Android注解小应用(通过注解findViewById)
注解
package com.example.annotationdemo.annotation;
import androidx.annotation.IdRes;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface InjectView {
@IdRes int value();
}
反射获取信息进行处理
package com.example.annotationdemo.inject;
import android.app.Activity;
import android.view.View;
import com.example.annotationdemo.annotation.InjectView;
import java.lang.reflect.Field;
public class InjectUtils {
public static void injectView(Activity activity){
Class<? extends Activity> aClass = activity.getClass();
try {
// 拿到类的所有属性
Field[] declaredFields = aClass.getDeclaredFields();
for(Field declaredField : declaredFields){
// 判断是否被注解标记
if (declaredField.isAnnotationPresent(InjectView.class)){
// 被注解标记获取注解信息
InjectView annotation = declaredField.getAnnotation(InjectView.class);
// 得到注解标记的值
int value = annotation.value();
View viewById = activity.findViewById(value);
// 允许操作
declaredField.setAccessible(true);
// 反射设置属性的值
declaredField.set(activity,viewById);
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
试用一下
package com.example.annotationdemo;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.TextView;
import com.example.annotationdemo.annotation.InjectView;
import com.example.annotationdemo.inject.InjectUtils;
public class MainActivity extends AppCompatActivity {
@InjectView(R.id.textview)
TextView tv;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
InjectUtils.injectView(this);
tv.setText("嘿咻");
}
}