之前博主讲xUtils的时候,介绍过注解,不过是蜻蜓点水,有兴趣的同学可以先移步到xUtils介绍2,今天我们就来详细解剖一下Android中注解的使用。
Java注解是附加在代码中的一些元信息,用于一些工具在编译、运行时进行解析和使用,起到说明、配置的功能。注解不会也不能影响代码的实际逻辑,仅仅起到辅助性的作用。包含在 java.lang.annotation 包中。
讲起注解,我为它分了四大块,元注解、内建注解、自定义注解,还有xUtils中为我们提供的注解方式,下面我们就一一剖析。
实例:
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Retention {
RetentionPolicy value();
}
//其中的@interface是一个关键字,在设计annotations的时候必须把一个类型定义为@interface,而不能用class或interface关键字,由以上的源码可以知道,他的elementType 可以有多个,一个注解可以为类的,方法的,字段的等等。
元注解,分为四种:包括 @Retention @Target @Document @Inherited四种。
@Retention:定义注解的保留策略。
通俗的讲,它表示一个注解类型会被保留到什么时候。
@Retention(RetentionPolicy.SOURCE)//注解仅存在于源码中,在class字节码文件中不包含。
@Retention(RetentionPolicy.CLASS)// 默认的保留策略,注解会在class字节码文件中存在,但运行时无法得。
@Retention(RetentionPolicy.RUNTIME)// 注解会在class字节码文件中存在,在运行时可以通过反射获取到。
@Target:定义注解的作用目标。
@Target(ElementType.TYPE) //接口、类、枚举、注解
@Target(ElementType.FIELD) //字段、枚举的常量
@Target(ElementType.METHOD) //方法
@Target(ElementType.PARAMETER) //方法参数
@Target(ElementType.CONSTRUCTOR) //构造函数
@Target(ElementType.LOCAL_VARIABLE)//局部变量
@Target(ElementType.ANNOTATION_TYPE)//注解
@Target(ElementType.PACKAGE) ///包
@Document:说明该注解将被包含在javadoc中。
会被Javadoc工具文档化。
@Inherited:说明子类可以继承父类中的该注解。
这个比较难理解,这里详细讲一下,一般用在自定义注解上。
我们自定义注解(Annotation)时,把自定义的注解标注在父类上不会被子类所继承,但是我们可以在定义注解时给我们自定义的注解标注一个@Inherited注解来实现注解继承。
注意点:
这种标有@Inherited注解的自定义的注解运用到类级别上和方法级别上是不一样的,如果把标有@Inherited注解的自宝义的注解标注在类级别上,子类则可以继承父类类级别的注解,反之,则不行。
实例:(标注在类级别)
1.定义一个自定义注解,包含一个方法value
@Inherited
@Retention(java.lang.annotation.RetentionPolicy.RUNTIME)
public @interface InheritedAnnotation {
String value();
}
2.定义一个带注解的抽象父类
@InheritedAnnotation(value = "parent AnnotarionParent ")
public abstract class AnnotarionParent {
@InheritedAnnotation(value = "parent abstractMethod ")
public abstract void abstractMethod();
@InheritedAnnotation(value = "Parent's doExtends")
public void doExtends() {
Log.d("dongmj"," AbstractParent doExtends ...");
}
}
3.定义一个子类继承上面父类
public class SubAnnotation extends AnnotarionParent{
@Override
public void abstractMethod() {
Log.d("dongmj","子类实现抽象父类的抽象方法");
}
}
4.测试:
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Class clazz = SubAnnotation.class; //1.获得子类class类型
try {
Method method = clazz.getMethod("abstractMethod", new Class[]{});//2获得方法
if(method.isAnnotationPresent(InheritedAnnotation.class)){//3.判断方法的注解类型
InheritedAnnotation ma = method.getAnnotation(InheritedAnnotation.class);//4.得到方法的注解
Log.d("dongmj","子类实现的抽象方法继承到父类抽象方法中的Annotation,其信息如下:");
Log.d("dongmj",ma.value()); //5.打印注解中的value方法
}else{
Log.d("dongmj","子类实现的抽象方法没有继承到父类抽象方法中的Annotation");
}
Method methodDoExtends = clazz.getMethod("doExtends", new Class[]{});
if(methodDoExtends.isAnnotationPresent(InheritedAnnotation.class)){
InheritedAnnotation mado = methodDoExtends.getAnnotation(InheritedAnnotation.class);
Log.d("dongmj","子类实现的抽象方法继承到父类抽象方法中的doExtends,其信息如下:");
Log.d("dongmj",mado.value());
}else{
Log.d("dongmj","子类实现的抽象方法没有继承到父类抽象方法中的doExtends");
}
if(clazz.isAnnotationPresent(InheritedAnnotation.class)){
InheritedAnnotation cla = clazz.getAnnotation(InheritedAnnotation.class);
Log.d("dongmj","子类继承到父类类上Annotation,其信息如下:");
Log.d("dongmj",cla.value());
}else{
Log.d("dongmj","子类没有继承到父类类上Annotation");
}
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
}
}
结果:
09-18 15:11:39.898 32178-32178/com.example.annotationtest D/dongmj: 子类实现的抽象方法没有继承到父类抽象方法中的Annotation
09-18 15:11:39.908 32178-32178/com.example.annotationtest D/dongmj: 子类实现的抽象方法继承到父类抽象方法中的doExtends,其信息如下:
09-18 15:11:39.908 32178-32178/com.example.annotationtest D/dongmj: Parent's doExtends
09-18 15:11:39.908 32178-32178/com.example.annotationtest D/dongmj: 子类继承到父类类上Annotation,其信息如下:
09-18 15:11:39.908 32178-32178/com.example.annotationtest D/dongmj: parent AnnotarionParent
Java本身内建了一些注解,下面我们来介绍一下我们在日常开发中比较常见的注解:
@Override、@Deprecated、@SuppressWarnings。
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}
这个注解可以被用来修饰方法,并且它只在编译时有效,在编译后的class文件中便不再存在。这个注解的作用我们大家都不陌生,那就是告诉编译器被修饰的方法是重写的父类的中的相同签名的方法,编译器会对此做出检查,若发现父类中不存在这个方法或是存在的方法签名不同,则会报错。
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(value={CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, PARAMETER, TYPE})
public @interface Deprecated {
}
从它的定义我们可以知道,它会被文档化,能够保留到运行时,能够修饰构造方法、属性、局部变量、方法、包、参数、类型。这个注解的作用是告诉编译器被修饰的程序元素已被“废弃”,不再建议用户使用。
Override就是用在方法上的注解,Deprecated是既可以用在方法上面,也可以用在类上面。
@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
@Retention(RetentionPolicy.SOURCE)
public @interface SuppressWarnings {
String[] value();
}
它能够修饰的程序元素包括类型、属性、方法、参数、构造器、局部变量,只能存活在源码时,取值为String[]。它的作用是告诉编译器忽略指定的警告信息,它可以取的值如下所示:
deprecation:忽略使用了废弃的类或方法时的警告;
unchecked:执行了未检查的转换;
fallthrough:swich语句款中case忘加break从而直接“落入”下一个case;
path:类路径或原文件路径等不存在;
serial:可序列化的类缺少serialVersionUID;
finally:存在不能正常执行的finally子句;
all:以上所有情况产生的警告均忽略。
用法:
@SuppressWarning(value={"deprecation", "unchecked"})
public void myMethos() {...}
在自定义注解时,有以下几点需要我们了解:
1)注解类型是通过”@interface“关键字定义的;
2)在”注解体“中,所有的方法均没有方法体且只允许public和abstract这两种修饰符号(不加修饰符缺省为public),注解方法不允许有throws子句;
3)注解方法的返回值只能为以下几种:原始数据类型), String, Class, 枚举类型, 注解和它们的一维数组,可以为方法指定默认返回值。
上面的实例也是自定义注解的栗子。再举个
/**
* 水果名称注解
* @author peida
*
*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface FruitName {
String value() default "";
}
/**
* 水果颜色注解
* @author peida
*
*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface FruitColor {
/**
* 颜色枚举
* @author peida
*
*/
public enum Color{ BULE,RED,GREEN};
/**
* 颜色属性
* @return
*/
Color fruitColor() default Color.GREEN;
}
/**
* 水果供应者注解
* @author peida
*
*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface FruitProvider {
/**
* 供应商编号
* @return
*/
public int id() default -1;
/**
* 供应商名称
* @return
*/
public String name() default "";
/**
* 供应商地址
* @return
*/
public String address() default "";
}
public class AnnotationParser {
public static void main(String[] args) {
try {
Class cls = AnnotationTest.class;
for (Method method : cls.getMethods()) {
MethodInfo methodInfo = method.getAnnotation(MethodInfo.class);
if (methodInfo != null) {
System.out.println("method name:" + method.getName());
System.out.println("method author:" + methodInfo.author());
System.out.println("method date:" + methodInfo.date());
System.out.println("method version:" + methodInfo.version());
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
关于这部分,主要是可以对findViewByid和onclick的优化,博主之前已经写过,有兴趣的同学移步xUtils注解介绍