Java入门篇——注解Annotation

在Java1.5以后,引入了注解,也称作元数据。作为新的特性,同时也是基础知识之一,我们应该学会使用这种用法,虽然反射会带来代码效率问题,但相比于它的优点,这种损失我们还是可以承受的。

元数据被定义为:描述数据的数据,对数据及信息资源的描述性信息。

我们可以认为注解的目的就是对数据添加的附加信息。在java源代码中添加注解,有助于减轻编写“样板”代码的负担(findViewById),更加干净易读的代码以及编译器类型检查。

注解的语法比较简单,使用一个@符号修饰代表的就是一个注解。Java 5内置的几种注解有

//当前的方法定义将覆盖超类中的方法
@Override
//代表被这个元数据修饰的元素已被废弃,使用已废弃的方法或对象编译器会发出警告
@Deprecated
//关闭不当的编译器警告信息,比如unchecked,未检查的类型等
@SuppressWarnings

自定义注解类型

使用@interface来定义注解类型

public @interface Test {
}

使用注解类型

//一般写法,比较优美
@Test
void test(){

}

//注解可以看做是一种修饰符,它的使用和修饰符几乎一模一样
// 不太好看,不建议
public static synchronized @Test void  test(){

}

上面简单定义了一个注解,但是一般我们定义的注解,还会定义一些注解的类型,Annotation有四种元注解类型,元注解专职负责注解其他的注解,详情可以看Java API里面的Annotation

# Retention 
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Retention {
    RetentionPolicy value();
}

# Target 
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Target {
    ElementType[] value();
}

# Documented
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Documented {
}

# Inherited 
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Inherited {
}

Rentention
Rentention定义该注解在哪一个级别保留该注解信息,可选的RetentionType参数包含

// 存在于Java源文件,注解被编译器丢弃
SOURCE
// 存在于Java源文件,以及经编译器后生成的Class字节码文件,但在运行时VM不再保留注解
CLASS
// 存在于源文件、编译生成的Class字节码文件,以及保留在运行时VM中,因此可通过反射读取注解
RUNTIME

Target
Target表示该注解可以用于什么地方,可能使用的参数ElementType包括

// 注解类型,表示这个注解只能用于注解类型
// 比如Target,Rentention,Inherited,Documented这些元注解都是用于注解类型的
ANNOTATION_TYPE
// 构造器声明
CONSTRUCTOR
// 字段声明(包括enum实例)
FIELD
// 局部变量声明
LOCAL_VARIABLE
// 方法声明
METHOD
// 包声明
PACKAGE
// 参数声明
PARAMETER
// 类,接口(包括注解类型)或者enum声明
TYPE

Documented
当前注解的元素会被javadoc工具进行文档化,那么在查看Java API文档时可查看该注解元素。

Inherited
允许子类继承父类中的注解

注解元素
在注解中还会包含一些元素表示值,当使用Class里面的方法分析处理注解的时候,程序就可以访问这些值。在注解中定义元素就像在普通接口中定义方法,但是注解可以使用default定义元素的默认值。对于没有元素的注解,我们可以把它作为标记来使用。比如被@Test标记的方法为测试方法
注解元素可用的类型包括如下几个

基本数据类型
String
Class
enum
Annotation
以上类型的数组

对于注解里面的元素,必须有一个确定的值,不能够使用null这种未定义的值作为默认值,所以我们可以使用空对象这样的概念来解决这个问题,比如定义空字符串作为字符串为null的情况。

解析注解
在很多ORM数据库框架中都使用了注解来定义Bean类,直接使用Bean类生成数据库表。比如ORMLite。

如果不对注解进行解析的话,其实注解就没什么意义了,可以通过Annotation中提供的API来访问注解。那么先来看一下Class提供给我们的用于解析注解的结构方法

// 如果当前元素包含指定的注解类型,则返回该注解对象,如果不存在则返回null
<A extends Annotation> getAnnotation(Class annotationClass)
// 返回这个元素上的所有注解
Annotation[] getAnnotations()
// 返回直接定义在这个元素上的注解
Annotation[] getDeclaredAnnotations()
// 如果当前这个元素包含指定的注解类型则返回true
boolean isAnnotationPresent(Class annotationClass)

在Android中也有通过注解实现的IOC,我们在编写程序的时候,如果xml里面有很多很多的控件,这样就需要写很多遍findViewById,不但写起来很累,而且很占空间,于是人们就想到了通过注解来减轻这样的编写“样板”代码的负担。

ViewInject注解类

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface ViewInject {
    int id() default -1;
}

BaseActivity基类,使用模板方法

public abstract class BaseActivity extends Activity {
    private Context mContext;

    private void inject() {
        Class activity = getClass();
        //获取Activity内所有的字段
        Field[] fields = activity.getDeclaredFields();
        for (Field field : fields) {
            field.setAccessible(true);
            // 获取指定的注解类型,如果返回null则跳过
            ViewInject viewInject = field.getAnnotation(ViewInject.class);
            if (viewInject != null) {
                // 获取指定的属性
                int value = viewInject.id();
                try {
                    // 反射获取findViewById方法
                    Method method = activity.getMethod("findViewById", int.class);
                    method.setAccessible(true);
                    // 调用该方法,因为findViewById要求是在Activity对象上的方法
                    Object object = method.invoke(mContext, value);
                    field.set(mContext, object);
                } catch (NoSuchMethodException e) {
                    e.printStackTrace();
                } catch (InvocationTargetException e) {
                    e.printStackTrace();
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    protected abstract int requestLayout();

    protected abstract void bindView();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(requestLayout());
        mContext = this;
        inject();
        bindView();
    }
}

MainActivity实现类

public class MainActivity extends BaseActivity {
    @ViewInject(id=R.id.text)
    private TextView textView;

    @Override
    protected int requestLayout() {
        return R.layout.layout_annotation;
    }

    @Override
    protected void bindView() {
        textView.setText("1131");
    }

}

现在大致上那些retrofit,dagger,butterknife使用的注解也是基于这个原理的罢,有空去好好研究一下这几个开源框架的源码,使用注解,我们应该还需要有类加载,泛型,反射等基础知识,才能够把注解玩的飞起。

你可能感兴趣的:(java基础)