注解是什么?简单说注解就是一种标注(标记、标识),没有具体的功能逻辑代码。也能够把注解理解为代码里的特殊标记,这些标记能够在编译,类加载,运行时被读取,并执行相应的处理。经过注解开发人员能够在不改变原有代码和逻辑的状况下在源代码中嵌入补充信息。html
JDK 中内置了如下注解:java
@Override 旨在通知编译器该方法是覆盖父类中声明的方法android
经过IDE快捷键实现接口方法和复写父类方法时,都会自动添加@Override注解。程序员
@Deprecated 标记已弃用的元素,不该再使用。将方法,类或字段标记为@Deprecated注解时,当用户使用该方法,类或字段时,编译器就会生成警告。数组
@SuppressWarnnings 关闭不当的编译器警告信息。安全
Java语言规范列出了两个类别:deprecation 和 unchecked。"unchecked"用于抑制未经检查的警告。"deprecation" 使用了不推荐的类或方法的警告。bash
@SafeVarargs注解应用于方法或构造函数时,断言代码不对其varargs参数执行可能不安全的操做。oracle
@SafeVarargs注解只能用在参数长度可变的方法或构造方法上,且方法必须声明为static或final,不然会出现编译错误。ide
从JVM对象的角度来看…与Object []几乎同样。函数
@FunctionalInterface 声明接口是函数式接口。
你用@FunctionalInterface定义了一个接口,而它却不是函数式接口的话,编译器将返回一个提示缘由的错误。
什么是函数式接口?函数式接口就是只定义一个抽象方法的接口,可是能够有多个默认方法或静态方法的接口。
Java 8容许在接口内声明静态方法和默认方法。默认方法是指提供接口方法的默认实现,用default关键字进行修饰。
@FunctionalInterface
public interface Predicate<T> {
boolean test(T t);
default Predicate<T> and(Predicate<? super T> other) {
Objects.requireNonNull(other);
return (t) -> test(t) && other.test(t);
}
default Predicate<T> negate() {
return (t) -> !test(t);
}
default Predicate<T> or(Predicate<? super T> other) {
Objects.requireNonNull(other);
return (t) -> test(t) || other.test(t);
}
static <T> Predicate<T> isEqual(Object targetRef) {
return (null == targetRef)
? Objects::isNull
: object -> targetRef.equals(object);
}
}
复制代码
Java8的谓词接口Predicate,其自己的除了惟一的抽象方法外,还定义了默认方法和静态方法。
适用于其余注解的注解称为元注解。在java.lang.annotation中定义了几种元注解类型。
@Retention 声明注解的保留策略。
查看Retention的源码:
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Retention {
RetentionPolicy value();
}
复制代码
由源码得知,Retention的值是一个RetentionPolicy类型的变量,而RetentionPolicy是一个枚举值,其值包括:
若是注解类型声明中不存在Retention注解,则Retention默认为 RetentionPolicy.CLASS。
不管什么时候使用指定的注解,都应使用Javadoc工具记录这些元素。 若是使用Documented注解类型声明,则其注解将成为带注解元素的公共API的一部分。
@Target注解用于限制能够应用该注解的Java元素类型。
查看Target的源码:
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Target {
ElementType[] value();
}
复制代码
由源码得知,Target的值是一个ElementType类型的数组变量,便可以同时设置多个值。而ElementType是一个枚举值,其值包括: Target注解指定如下元素类型之一做为其值:
ElementType.TYPE_PARAMETER 和 ElementType.TYPE_USE属于Java 8的新特性,具体看下面Java8 注解新特性。
@Inherited注解代表注解类型能够从超类继承。当用户查询注解类型而且该类没有此类型的注解时,将查询类的超类以获取注解类型。将重复此过程,直到找到此类型的注解,或者到达类层次结构(对象)的顶部。若是没有超类具备此类型的注解,则查询将指示相关类没有此类注解。此注解仅适用于类声明。
Java 8在两个方面对注解机制进行了改进,分别为:
在Java 8以前,只有声明能够被注解。Java 8中,注解能够写在使用类型的任何地方,例如括new操做符、类型转换、instanceof检查、泛型类型参数,以及implements和throws子句。例如:
//列表泛型
List<@NonNull Car> cars = new ArrayList<>();
//对象类型转化时
myString = (@NonNull String) str;
//使用 implements 表达式时
class MyList<T> implements @ReadOnly List<@ReadOnly T>{
}
//使用 throws 表达式时
public void validateValues() throws @Critical ValidationFailedException{
}
复制代码
定义一个类型注解(Type Annotation)的方法与普通的 Annotation 相似,只须要指定 Target 为 ElementType.TYPE_PARAMETER 或者 ElementType.TYPE_USE,或者同时指定这两个 Target。
@Target({ElementType.TYPE_PARAMETER, ElementType.TYPE_USE})
public @interface daqiAnnotation {
}
复制代码
ElementType.TYPE_PARAMETER 表示这个注解能够用在类型变量的声明语句前。
ElementType.TYPE_USE 表示这个注解能够用在全部使用类型的任何语句中(如:泛型,类型转换等)
Java 8 经过引入类型注解(Type Annotation),使得开发者能够在更多的地方使用 Annotation,从而可以更全面地对代码进行分析以及进行更强的类型检查。
Java8 以前禁止对一样的注解类型声明屡次。在实际应用中,可能会出现须要对同一个声明式或者类型加上相同的 Annotation(包含不一样的属性值)的状况。
@interface Author { String name(); }
@Author(name="Raoul") @Author(name="Mario") @Author(name="Alan")
class Book{ }
复制代码
能够声明一个新的注解,其包含但愿重复的注解数组。
@interface Author { String name(); }
@interface Authors {
Author[] value();
}
@Authors({ @Author(name="Raoul"), @Author(name="Mario") ,@Author(name="Alan")})
class Book{}
复制代码
Java8 以后,当一个注解在设计之初就是可重复的,能够经过两种途径实现:
@Repeatable示例:
@Repeatable(Authors.class)
@interface Author { String name(); }
@interface Authors {
Author[] value();
}
@Author(name="Raoul") @Author(name="Mario") @Author(name="Alan")
class Book{ }
复制代码
编译时,Book类会被认为使用了@Authors({@Author(name="Raoul"), @Author(name =”Mario”), @Author(name=”Alan”)})的形式进行注解。因此,能够把重复注解(Repeating Annotation)当作是一种语法糖,它提供了Java程序员以前惯用的功能。
因为兼容性的缘故,重复注解(Repeating Annotation)并非全部新定义的 Annotation 的默认特性,须要开发者根据本身的需求决定新定义的 Annotation 是否能够重复标注。
以android最为熟悉的findVIewById 和 onClick为例,定义两个运行时保存的注解:
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface findViewById {
int value();
}
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface onClickById {
int value();
}
复制代码
因为onClickById 和 findViewById 的元注解Retention的值为RetentionPolicy.RUNTIME,则表示在程序运行时,能够获取到该注解。
经过注解中携带的value,对属性或方法进行反射,从而实现属性初始化和点击事件绑定的目的。
public class daqiAnnotationUtils {
public static void inject(Activity activity) {
injectFiled(activity);
injectEvent(activity);
}
private static void injectFiled(Activity activity){
//获取Activity的全部属性
Field[] fields = activity.getClass().getDeclaredFields();
//寻找有findViewById注解的属性
for (Field field : fields) {
findViewById viewById = field.getAnnotation(findViewById.class);
if(viewById != null){
//经过findViewById注解中的值,经过activity#findViewById找到对应的View
View view = activity.findViewById(viewById.value());
//设置能够反射私有变量
field.setAccessible(true);
try {
//将获取到的view赋值到对应的变量中。
field.set(activity,view);
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
}
private static void injectEvent(final Activity activity){
//获取Activity的全部方法
Method[] methods = activity.getClass().getDeclaredMethods();
//寻找有onClickById注解的属性
for (Method method : methods) {
onClickById clickById = method.getAnnotation(onClickById.class);
if (clickById != null){
//经过onClickById注解中的值,经过activity#findViewById找到对应的View
final View view = activity.findViewById(clickById.value());
if (view != null) {
final Method mMethod = method;
//设置View#setOnClickListener
view.setOnClickListener(new View.OnClickListener(){
@Override
public void onClick(View v) {
try {
mMethod.setAccessible(true);
//反射执行方法
mMethod.invoke(activity);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
}
}
}
}
复制代码
定义两个个TextView,一个用做展现文字,对应的id为R.id.nameText;一个用做点击按钮,对应id为R.id.textBtn。
经过daqiAnnotationUtils#inject(Activity)初始化activity中有findViewById注解的变量,并将有onClickById的方法与其对应的组件实现点击监听的绑定。
public class daqiActivity extends FragmentActivity {
@findViewById(R.id.nameText)
private TextView mName;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_daqi);
//初始化注解的变量和方法
daqiAnnotationUtils.inject(this);
//修更名称
mName.setText("daqi");
}
@onClickById(R.id.textBtn)
public void toastName(){
Toast.makeText(daqiActivity.this,
"daqi",Toast.LENGTH_SHORT).show();
}
}
复制代码
《 Java8 实战 》