注解的了解

微信公众号:Lucidastar
如有问题或建议,请公众号留言
最近更新:2019-02-26

注解的学习

最近在学习dagger2,里头涉及到了很多的注解,感觉注解很简单,但是又说不上来,补充补充基础的知识吧。
在平常的开发中,注解真的是无处不在,尤其在框架中,一个注解就可以做很多的事情,有时候就只记住了就应该这样写,这样配置,但是,脑袋中还是有很多的问号。所以让我们来了解一下。

定义:
注解(也被称为元数据)为我们在代码中添加信息提供了一种形式化的方法,使我们可以在稍后某个时刻非常方便的使用这些数据。
注解是众多引入到Java SE5中的重要的语言变化之一。它们可以提供用来完整地描述程序所需要的信息,而这些信息时无法用Java来表达的。因此,注解使得我们能够以将由编译器来测试和验证的格式,存储有关程序的额外信息。注解可以用来生产描述符文件,甚至或者新的类定义,并且有助于减轻编写“样板”代码的负担。通过使用主角,我们可以将这些元数据保存在Java源代码中,并利用annotation API为自己的注解构造处理工具。

优点:
更加干净易读的代码以及编译期类型检查等。
Java SE5中内置了三种,定义在java.lang中的注解:
1、@Override,表示当前的方法定义将覆盖超类中的方法。如果不小心拼写错误,或者方法签名对不上被覆盖的方法,编译器就会错误提示。
2、@Deprecated,使用了注解为它的元素,那么编译器会发出警告信息。
3、@SuppressWarnings,关闭不当的编译器警告信息。

元注解:
指注解的注解,包含@Retention @Target @Document @Inherited四种

@Retention(中文:保留):什么时候保留这些信息,它的参数一共就三个:

①RetentionPolicy.SOURCE – 在编译阶段丢弃。这些注解在编译结束之后就不再有任何意义,所以它们不会写入字节码。@Override, @SuppressWarnings都属于这类注解。

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)//使用在源码上
public @interface Override {
}

②RetentionPolicy.CLASS – 在类加载的时候丢弃。在字节码文件的处理中有用。注解默认使用这种方式。

@Documented
@Retention(RetentionPolicy.CLASS)//class文件中
@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.LOCAL_VARIABLE})
public @interface Nullable {
}

③RetentionPolicy.RUNTIME– 始终不会丢弃,运行期也保留该注解,因此可以使用反射机制读取该注解的信息。我们自定义的注解通常使用这种方式。

Java源文件(.java文件)---->.class文件---->内存中的字节码
这是这三种的区别

@Target(中文:目标,标记):注解用在什么地方,它的参数是一个数组:

①ElementType.TYPE: 用于描述类、接口或enum声明
②ElementType.FIELD: 用于描述实例变量
③ElementType.METHOD 方法声明
④ElementType.PARAMETER 参数声明
⑤ElementType.CONSTRUCTOR 构造声明
⑥ElementType.LOCAL_VARIABLE 局部变量声明
⑦ElementType.ANNOTATION_TYPE 另一个注释
⑧ElementType.PACKAGE 记录java文件的package信息

@Document 将注解包含在Javadoc中

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

注解的使用(使用Java编程思想中的例子来说明)
定义一个注解:

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface UseCase {
    public int id();
    public String description () default "no description";
}

使用注解:

public class PasswordUtils {
    @UseCase(id = 47, description = "Passwords must contain at least one numeric")
    public boolean validatePassword(String password) {
        return (password.matches("\\w*\\d\\w*"));
    }
    @UseCase(id = 48)
    public String encryptPassword(String password) {
        return new StringBuilder(password).reverse().toString();
    }
    @UseCase(id = 48,description = "New passwords can't equal previously used ones")
    public boolean checkForNewPassword(List<String> prevPasswords ,String password){
        return !prevPasswords.contains(password);
    }
}

对注解的处理:

public static void main(String[] args) {
    List<Integer> useCases = new ArrayList<Integer>();
    Collections.addAll(useCases, 47, 48, 49, 50);
    trackUseCases(useCases, PasswordUtils.class);

    Integer max = Collections.max(useCases);
    System.out.println(max);
}

public static void trackUseCases(List<Integer> useCases, Class<?> cl) {
    for (Method m : cl.getDeclaredMethods()) {
        UseCase uc = m.getAnnotation(UseCase.class);
        if (uc != null) {
            System.out.println("Found Use Case:" + uc.id() + " "
                    + uc.description());
            useCases.remove(new Integer(uc.id()));
        }
    }
    for (int i : useCases) {
        System.out.println("Warning: Missing use case-" + i);
    }
}

注解这算就学完了,明白了吗?说实在,我也不明白。我们来推测一下butterknife这个框架,应用在Android绑定view中,我们来看一下他是怎么使用的?
绑定
现在mTvTest就已经实例化了,为我们省略了findViewById(),我们看一下BindView的注解。

@Retention(RUNTIME) @Target(FIELD)
public @interface BindView {
  /** View ID to which the field will be bound. */
  @IdRes int value();
}

保留方式是使用的内存中的字节码(RUNTIME) target使用的是实例变量,然后我们把注解声明在了变量(控件变量)上,然后使用ButterKnife.bind(this);就实例化了控件,我们来看一下它是如何处理的

Class<?> targetClass = target.getClass();
if (debug) Log.d(TAG, "Looking up binding for " + targetClass.getName());
Constructor<? extends Unbinder> constructor = findBindingConstructorForClass(targetClass);//获取类中的公共方法
if (constructor == null) {
  return Unbinder.EMPTY;
}
//noinspection TryWithIdenticalCatches Resolves to API 19+ only type.
try {
  return constructor.newInstance(target, source);
}

@Nullable 
@CheckResult 
@UiThread
private static Constructor<? extends Unbinder> findBindingConstructorForClass(Class<?> cls) {
  Constructor<? extends Unbinder> bindingCtor = BINDINGS.get(cls);
  if (bindingCtor != null || BINDINGS.containsKey(cls)) {
    if (debug) Log.d(TAG, "HIT: Cached in binding map.");
    return bindingCtor;
  }
  String clsName = cls.getName();
  if (clsName.startsWith("android.") || clsName.startsWith("java.")
      || clsName.startsWith("androidx.")) {
    if (debug) Log.d(TAG, "MISS: Reached framework class. Abandoning search.");
    return null;
  }
  try {
    Class<?> bindingClass = cls.getClassLoader().loadClass(clsName + "_ViewBinding");
    //noinspection unchecked
    bindingCtor = (Constructor<? extends Unbinder>) bindingClass.getConstructor(cls, View.class);
    if (debug) Log.d(TAG, "HIT: Loaded binding class and constructor.");
  } catch (ClassNotFoundException e) {
    if (debug) Log.d(TAG, "Not found. Trying superclass " + cls.getSuperclass().getName());
    bindingCtor = findBindingConstructorForClass(cls.getSuperclass());
  } catch (NoSuchMethodException e) {
    throw new RuntimeException("Unable to find binding constructor for " + clsName, e);
  }
  BINDINGS.put(cls, bindingCtor);
  return bindingCtor;
}

主要能看懂注解代码就可以,注解一般在设计框架时用的比较多一些。
我的公众号:Lucidastar

image

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值