微信公众号: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