(转载公司内部论坛本人文章2019.12.06)
Java的注解之前使用注解比较少,特此去了解了一下,并用注解对JsBridge代码重构了一下。重构后跳转Activity传参的方式如下:
改造前:
改造后:
其中 @JsParameter(“xxx”) 里面参数和json的key对应,然后BaseActivity里做了给加注解的成员变量自动赋值逻辑。可以看到,加了注解后,逻辑更加清晰了。之前那种方式,如果开发一不留意改了变量的命名,就会造成JsBridge跳转失败,加了注解后,可以规避这个问题。
BaseActivity关键代码和注解实现如下:
private void setJsField() {
if (getJsJSONObject() == null) {
return;
}
Field[] fields = getClass().getDeclaredFields();
Set<Map.Entry<String, Object>> entrySet = getJsJSONObject().entrySet();
int noSetFieldSize = entrySet.size();
for (Field field : fields) {
if (field.isAnnotationPresent(JsParameter.class)) {
JsParameter jsParameter = field.getAnnotation(JsParameter.class);
setFor : for (Map.Entry<String, Object> entry : entrySet) {
if (TextUtils.equals(entry.getKey(), jsParameter.value())) {
try {
field.setAccessible(true);
field.set(this, entry.getValue());
noSetFieldSize--;
break setFor;
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
}
if (noSetFieldSize <= 0) {
break;
}
}
}
/**
* 注解实现代码
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
@Documented
public @interface JsParameter {
String value() default "";
}
下面是对注解一些浅析,有兴趣的可以继续往下看。
什么是注解?
官方解释:
注解 (Annotation),也叫元数据。一种代码级别的说明。它是 JDK1.5 及以后版本引入的一个特性,与类、接口、枚举是在同一个层次。它可以声明在包、类、字段、方法、局部变量、方法参数等的前面,用来对这些元素进行说明,注释。
个人理解:
所谓的注解其实就是给类,变量,方法等加注释,让一些工具在编译、运行时进行解析和使用,起到说明、配置的功能。如给一个类或方法加@Deprecated,则可以告诉编译器该类或方法属于已过期。
注解定义
注解通过 @interface 关键字进行定义。
public @interface JsParameter {
}
注解的属性
注解的属性也叫做成员变量。注解只有成员变量,没有方法。注解的成员变量在注解的定义中以“无形参的方法”形式来声明,其方法名定义了该成员变量的名字,其返回值定义了该成员变量的类型。在注解中定义属性时它的类型必须是 8 种基本数据类型外加类、接口、注解及它们的数组。
public @interface TestAnnotation {
public int id() default -1;
public String msg() default "Hi";
}
上面代码定义了 TestAnnotation 这个注解中拥有 id 和 msg 两个属性,默认值分别是-1,“Hi”。在使用的时候,我们应该给它们进行赋值。赋值的方式是在注解的括号内以value="" 形式,多个属性之前用,隔开。不赋值则取默认值。
@TestAnnotation(id=3,msg="hello annotation")
public class Test {
}
另外,如果一个注解内仅仅只有一个名字为 value 的属性时,应用这个注解时可以直接接属性值填写到括号内。也就是我上面那个例子,可以直接注解 @JsParameter(“id”)
public @interface JsParameter {
String value() default "";
}
@JsParameter("id")
private String mMerchantId;
什么是元注解?
元注解是可以注解到注解上的注解,或者说元注解是一种基本注解,但是它能够应用到其它的注解上面。
元注解有 @Retention、@Documented、@Target、@Inherited、@Repeatable 5 种。
@Retention
Retention 的英文意为保留期的意思。当 @Retention 应用到一个注解上的时候,它解释说明了这个注解的存活时间。
它的取值如下:
RetentionPolicy.SOURCE 注解只在源码阶段保留,在编译器进行编译时它将被丢弃忽视。
RetentionPolicy.CLASS 注解只被保留到编译进行的时候,它并不会被加载到 JVM 中。
RetentionPolicy.RUNTIME 注解可以保留到程序运行的时候,它会被加载进入到 JVM 中,所以在程序运行时可以获取到它们。
如我上面的例子,注解可以保留到运行时中,这样才能在运行时中获取到value值:
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
@Documented
public @interface JsParameter {
String value() default
}
@Documented
顾名思义,这个元注解肯定是和文档有关。它的作用是能够将注解中的元素包含到 Javadoc 中去。
@Target
Target 是目标的意思,@Target 指定了注解运用的地方,可以理解为该注解能注解什么。
比如 @Target(ElementType.FIELD) 表示该注解只能注解成员变量。
其他还有 ElementType.METHOD,ElementType.TYPE,ElementType.CONSTRUCTOR 等类型
@Inherited
Inherited 是继承的意思,一个超类@Inherited的注解注解了,那么该超类的子类会默认拥有该注解,有点绕,可以下下面例子
@Inherited
@Retention(RetentionPolicy.RUNTIME)
@interface Test {}
@Test
public class A {}
public class B extends A {}
注解 Test 被 @Inherited 修饰,之后类 A 被 Test 注解,类 B 继承 A,类 B 也拥有 Test 这个注解。
@Repeatable
Repeatable 自然是可重复的意思。@Repeatable 是 Java 1.8 才加进来的,所以算是一个新的特性。
什么样的注解会多次应用呢?通常是注解的值可以同时取多个。举个例子
@interface Persons {
Person[] value();
}
@Repeatable(Persons.class)
@interface Person{
String role default "";
}
@Person(role="artist")
@Person(role="coder")
@Person(role="PM")
public class SuperMan{
}
注意上面的代码,@Repeatable 注解了 Person。而 @Repeatable 后面括号中的类相当于一个容器注解。什么是容器注解呢?就是用来存放其它注解的地方。它本身也是一个注解。
我们再看看代码中的相关容器注解。
@interface Persons {
Person[] value();
}
按照规定,它里面必须要有一个 value 的属性,属性类型是一个被 @Repeatable 注解过的注解数组,注意它是数组。我们可能对于 @Person(role=”PM”) 括号里面的内容感兴趣,它其实就是给 Person 这个注解的 role 属性赋值为 PM 。
注解的提取
一般我们在定义和使用注解后,还需要提取注解,也就是怎么能在代码中利用到注解的值。
其实还是要利用到反射。
注解通过反射获取。在Class 对象中有如下方法操作注解Annotation的:
isAnnotationPresent() 方法判断它是否应用了某个注解
getAnnotation(Class annotationClass) 方法来获取 Annotation 对象。
getAnnotations() 方法返回注解到这个元素上的所有注解。
然后利用Annotation对象获得属性值。
如果获取到的 Annotation 如果不为 null,则就可以调用它们的属性方法了。比如我重构的例子。
@JsParameter("id")
private String mMerchantId;
@JsParameter("tab")
private int mTab;
private void setJsField() {
if (getJsJSONObject() == null) {
return;
}
Field[] fields = getClass().getDeclaredFields();
Set<Map.Entry<String, Object>> entrySet = getJsJSONObject().entrySet();
int noSetFieldSize = entrySet.size();
for (Field field : fields) {
if (field.isAnnotationPresent(JsParameter.class)) {
JsParameter jsParameter = field.getAnnotation(JsParameter.class);
setFor : for (Map.Entry<String, Object> entry : entrySet) {
if (TextUtils.equals(entry.getKey(), jsParameter.value())) {
try {
field.setAccessible(true);
field.set(this, entry.getValue());
noSetFieldSize--;
break setFor;
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
}
if (noSetFieldSize <= 0) {
break;
}
}
}
其中 jsParameter.value() 的值分别是id和tab。
以上是本人对注解的一些浅析,如有写得不对的地方,欢迎指出。
参考资料:
https://www.jianshu.com/p/420d0f73809e