从注解到解释器

从注解到解释器

反射(Reflection)

  • JAVA反射机制,指在“运行状态”下,
    • 对于任意一个类,都能够知道这个类的所有属性和方法;
    • 对于任意一个对象,都能够调用它的任意一个方法和属性;
  • Java反射机制主要提供了几个功能:
    • 在运行时判断任意一个对象所属的类:obj.getClass()
    • 在运行时构造任意一个类的对象:getClass().newInstance()
    • 在运行时判断任意一个类所具有的成员变量和方法:getClass().getDeclaredFields()getDeclaredMethods()
    • 在运行时调用任意一个对象的方法:method.invoke(obj, params…)
    • 在运行时为任意一个属性赋值:field.set(obj, value)

Class 对象

  • .class 文件在程序运行时会被 ClassLoader 加载到虚拟机中;
  • 当一个类被加载以后,JVM就会在内存中自动产生一个 Class 对象,通过它能够获悉整个类的结构。
/**
* 获取Class对象的三种方式
*/
public void Class<?> getClassObj() {
// 根据类名获取Class对象
Class<?> clazz1 = Book.class;
// 根据对象获取Class对象
Book book = new Book();
Class<?> clazz2 = people.getClass();
// 根据完整类名获取Class对象
try {
Class<?> clazz3 = Class.forName(“com.yuyh.demo.Book”);
} catch (ClassNotFoundException e) {
}
return clazz1; // clazz2 clazz3
}

构造实例

/**
* 调用类的构造函数
*/
public static Object getObject() {
try {
// 获取类的Class对象
Class<?> clz = getClassObj();
// 获取类对象的Constructor
Constructor<?> constructor = clz.getConstructor(String.class, int.class, String.class);
// 在使用时取消private权限检查
constructor.setAccessible(true);
// 通过 Constructor 来创建对象
Object obj = constructor.newInstance(“xxxx”, 3, “pppp”);
return obj;
} catch (Exception e) {
}
return null;
}

方法调用

public void getDeclaredMethods() {
Book book = (Book) getObject();
// 获取到类中的所有方法(不包含从父类继承的方法)
Method[] methods = book.getClass().getDeclaredMethods();
for (int i = 0; i < methods.length; i++) {
Log.i(TAG, "method[" + i + "] = " + methods[i].getName());
}
try {
// 获取类中的某个方法
Method method = book.getClass().getDeclaredMethod("setAuthor", String.class);
// 判断是否是public方法
Modifier.isPublic(method.getModifiers());
Modifier.isPrivate(method.getModifiers());
Modifier.isProtected(method.getModifiers());
// 获取该方法的参数类型列表
Parameter[] paramTypes = method.getParameters();
Log.i(TAG, "befor= " + book.getAuthor());
method.invoke(book, "yyh"); // 执行该方法
Log.i(TAG, "after= " + book.getAuthor());
} catch (Exception e) {
Log.e(TAG, e.toString());
}
}

属性赋值

public void voitDeclaredFields() {
Book book = (Book) getObject();
// 获取所有属性,不包括父类
Field[] fields = book.getClass().getDeclaredFields();
for (int i = 0; i < fields.length; i++) {
Log.i(TAG, "fields[" + i + "] = " + fields[i].getName());
}
try {
// 获取当前类的某个属性
Field field = book.getClass().getDeclaredField("author");
// 获取属性值
Log.i(TAG, "before = " + field.get(book));
// 设置属性值
field.set(book, "yuyh");
Log.i(TAG, "after = " + field.get(book));
} catch (Exception e) {
e.printStackTrace();
}
}

获取父类或接口

/**
* 获取对象的父类
*/
public static void getSuperClass() {
Book book = new Book();
Class<?> superClass = book.getClass().getSuperclass();
while (superClass != null) {
Log.i(TAG, "superClass = " + superClass.getName());
superClass = superClass.getSuperclass(); // 循环获取上一层父类(如果存在),至少存在一层java.lang.Object
}
}
/**
* 获取对象实现的接口
*/
public static void getInterface() {
// 获取该类实现的所有接口
Class<?>[] interfaces = Book.class.getInterfaces();
for (int i = 0; i < interfaces.length; i++) {
Log.i(TAG, "interfaces[" + i + "] = " + interfaces[i].getName());
}
}

注解(Annotation)

  • 注解 — 相当于某种标记,添加注解相当于为程序的某个地方打上一个标记。那么,程序从源代码到编译再到运行的各个阶段,可以去获取某个位置是否有这个标记,若有的话,就去执行相应的事情。

  • 注解位置

    • 方法,方法参数
    • 属性
  • 注解分类

    • 元注解
    • 內建注解
    • 自定义注解

元注解

元注解表示注解在其他注解之上的注解,Java中有四个元注解,分别是:

  • @Documented — 表示该类型的注解应该被 javadoc工具记录。默认情况下,javadoc生成文档是不包括注解的。
  • @Target — 表示该类型的注解可以注解的程序元素的范围。包括TYPE, METHOD, CONSTRUCTOR, FIELD 等。
  • @Inherited — 表示该类型的注解是否可以被继承。
  • @Retention — 表示该类型的注解会被保留到那个阶段。
    • RetentionPolicy.SOURCE — 源代码保留,编译时将会忽略
    • RetentionPolicy.CLASS — 编译时保留,存在于.class文件,通过ClassLoader向内存中加载字节码文件时候,JVM会去掉相应的注解。
    • RetentionPolicy.RUNTIME — 运行时保留,存在于字节码,可通过反射调用

內建注解

內建注解可以认为jdk提供的几个注解,分别是

  • @Override — 覆盖重写父类的方法
  • @Deprecated — 方法过时标记
  • @SurppressWarnings — 忽略警告
    • 例如:@SuppressWarnings({ ‘unchecked’, ‘deprecation’ })

自定义注解

用 @interface 标识

示例1:

// 1. 定义注解
@IntDef({
HttpMethod.GET,
HttpMethod.POST,
HttpMethod.PUT,
HttpMethod.DELETE
})
@Target({ElementType.PARAMETER}) // 只能作用在参数上
@Retention(RetentionPolicy.SOURCE) // 源代码级别
public @interface HttpMethod {
int GET = 0;
int POST = 1;
int PUT = 2;
int DELETE = 3;
}
// 2. 方法参数用@HttpMethod注解,用于传参约束
public void execute(@HttpMethod int method, Map<String,String> params){ … }
// 3. 调用时传参
execute(HttpMethod.GET, null);

示例2:

// 1. 定义注解
@Target(ElementType.FIELD) // 作用在属性上
@Retention(RetentionPolicy.RUNTIME) // 运行时生效,可通过反射获取
public @interface Column {
boolean nullable() default false;
}
// 2. 属性用@Column注解
@Column(nullable = true)
protected String cover;
// 3. 反射调用
try {
Field field = getClass().getField(“cover");
if (field.isAnnotationPresent(Column.class)) { // 判断是否有该注解
Column col = field.getAnnotation(Column.class);// 获取注解
boolean canBeNull = col.nullable();
if (!canBeNull) {
// do something
}
}
} catch (Exception e) {
e.printStackTrace();
}

注解处理器

  • 在编译时扫描和处理注解
  • 辅助生成Java代码
  • android-apt、AnnotationProcessor
    • android-apt只支持javac编译
    • annotationProcessor既支持javac同时也支持jack(jdk8)编译。
  • 最终编译出来的apk,不包含注解处理器的代码
    项目结构:
  • annotation:声明所使用的自定义注解
  • api:注解框架的具体实现
  • compiler:注解处理器,在编译期间通过反射处理注解,并生成代码
  • app:用于测试注解框架的demo

Element 元素:

  • PackageElement:包
  • TypeElement:类
  • VariableElement:类里面的属性
  • ExecutableElement:类里面的方法

案例: View注入处理过程实现

  • (1) 获取程序所有使用 @InjectView 注解的集合:roundEnv.getElementsAnnotatedWith(InjectView.class);
  • (2) 遍历 Element 集合
  • (3) 获取包名: String packageName = mElementUtils.getPackageOf(element).getQualifiedName().toString();
  • (4) 获取全类名:String enclosingName = (TypeElement) element.getEnclosingElement().getQualifiedName().toString();
  • (5) 获取属性类型:TypeMirror fieldType = element.asType();
  • (6) 获取属性名称:String fieldName = element.getSimpleName().toString();
  • (7) 通过注解获取ViewId值:int id = element.getAnnotation(InjectView.class).value();
  • (8) 以类名做区分,把 Element 归类,也就是每个有用到该注解的类对应一个 List
  • (9) 对这些用到注解的类,生成一个依赖注入类,并添加 findViewById 相关代码

 

评论

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×