Java注解学习记录(反射也能实现ButterKnife)
文章目录
- 什么是注解(Annotation)
- 注解有什么用
- 注解怎么用
- 注解关键字 @interface
- 注解属性的数据类型
- 元注解
- @Retention
- @Target
- @Documented
- @Inherited
- @Repeatable
- Java内置的注解
- 如何获取注解
- 反射获取运行期注解
- 结合反射与注解实现类似ButterKnife的功能
- 反射处理注解的缺点
- APT处理编译期注解
什么是注解(Annotation)
官方:Java 注解用于为 Java 代码提供元数据。作为元数据,注解不直接影响你的代码执行,但也有一些类型的注解实际上可以用于这一目的。Java 注解是从 Java1.5 开始添加到 Java 的。
通俗:注解就是给代码贴上标签。
注解有什么用
提供信息给编译器: 编译器可以利用注解来探测错误和警告信息
编译阶段时的处理: 软件工具可以用来利用注解信息来生成代码、Html文档或者做其它相应处理。
运行时的处理: 某些注解可以在程序运行的时候接受代码的提取
注解怎么用
要牢记,只要用到注解,必然有三点:定义注解;使用注解;获取注解。
注解关键字 @interface
//定义注解
@Retention(RetentionPolicy.RUNTIME)
public @interface A {String a();
}//使用注解
@A(a = "test")
public class Test { }//获取注解
Annotation[] annotation = Test.class.getAnnotations();
System.out.println(Arrays.toString(annotation));
注解属性的数据类型
- 八种基本数据类型
- String
- 枚举
- Class
- 注解类型
- 以上类型的一维数组
元注解
元注解是可以注解到注解上的注解,或者说元注解是一种基本注解,但是它能够应用到其它的注解上面。
元标签有 @Retention、@Documented、@Target、@Inherited、@Repeatable 5 种。
@Retention
Retention 意为保留期。当 @Retention 应用到一个注解上的时候,它解释说明了这个注解的的存活时间。
RetentionPolicy.SOURCE 注解只在源码阶段保留,在编译器进行编译时它将被丢弃忽视。
RetentionPolicy.CLASS 注解只被保留到编译进行的时候,它并不会被加载到 JVM 中。
RetentionPolicy.RUNTIME 注解可以保留到运行时,所以在程序运行时可以获取到它们
//RetentionPolicy.SOURCE ==========================================================
@Retention(RetentionPolicy.SOURCE)
public @interface A { String s() default "我是默认值"; }//Test.java
@A()
public class Test { }//Test.class,@A()被磨掉了
public class Test {public Test() { }
}//RetentionPolicy.CLASS ==========================================================
@Retention(RetentionPolicy.CLASS)
public @interface A { String s() default "我是默认值"; }//Test.class,@A()还在
@A
public class Test {public Test() { }
}//但是运行时却获取不到注解A
Annotation[] annotation = Test.class.getAnnotations();
System.out.println(Arrays.toString(annotation)); //输出 -> []//RetentionPolicy.RUNTIME ==========================================================
@Retention(RetentionPolicy.RUNTIME)
public @interface A { String s() default "我是默认值"; }//运行时获取到注解A了
Annotation[] annotation = Test.class.getAnnotations();
System.out.println(Arrays.toString(annotation)); //输出 -> [@com.llk.kt.annotation.A(s=我是默认值)]
@Target
Target 意为目标,@Target 指定了注解的使用的场景
public enum ElementType {/** 用于描述类、接口(包括注解类型) 或enum声明 */TYPE,/** 用于描述属性 */FIELD,/** 用于描述方法 */METHOD,/** 用于描述参数 */PARAMETER,/** 用于描述构造器 */CONSTRUCTOR,/** 用于描述局部变量 */LOCAL_VARIABLE,/** 用于描述注解类型 */ANNOTATION_TYPE,/** 用于描述包 */PACKAGE,/*** 用来标注类型参数* @since 1.8*/TYPE_PARAMETER,/*** 能标注任何类型名称* @since 1.8*/TYPE_USE
}
@Documented
这个元注解与文档有关,它的作用是能够将注解中的元素包含到 Javadoc 中去。(基本用不上)
@Inherited
Inherited 意为继承,如果父注解了@Inherited,那么它的子类没有被任何注解应用的话,那么这个子类就继承了父类的注解。
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface A {String a() default "赛亚人";
}@A(a = "超级赛亚人")
public class Test { }public class Test2 extends Test{}//输出 -> [@com.llk.kt.annotation.A(a=超级赛亚人)]
//如果没有加@Inherited 输出 -> []
Annotation[] annotation = Test2.class.getAnnotations();
System.out.println(Arrays.toString(annotation));
@Repeatable
Repeatable 意为可重复,@Repeatable 是 Java 1.8 才加进来的。
解决同一个注解不能重复在同一类/方法/属性上使用的问题
@Retention(RetentionPolicy.RUNTIME)
public @interface B {A[] value();
}@Repeatable(B.class)
public @interface A {String s() default "请添加描述";
}@A(s = "超级赛亚人")
@A(s = "奥特曼")
@A(s = "橡胶果实能力者")
public class Test { }//输出 -> [@com.llk.kt.annotation.B(value=[@com.llk.kt.annotation.A(s=超级赛亚人), @com.llk.kt.annotation.A(s=奥特曼), @com.llk.kt.annotation.A(s=橡胶果实能力者)])]
Annotation[] annotation = Test.class.getAnnotations();
System.out.println(Arrays.toString(annotation));
Java内置的注解
- @Override - 检查该方法是否是重写方法。如果发现其父类或者是引用的接口中并没有该方法时,会报编译错误。
- @Deprecated - 标记过时方法。如果使用该方法,会报编译警告。
- @SuppressWarnings - 指示编译器去忽略注解中声明的警告。
- @FunctionalInterface - Java 8 开始支持,标识一个匿名函数或函数式接口。
如何获取注解
反射获取运行期注解
AnnotatedElement接口是获取注解信息的接口
public interface AnnotatedElement {//如果指定类型的注释存在于此元素上,则返回 true,否则返回 false。
public default boolean isAnnotationPresent(Class<? extends Annotation> annotationClass) { throw new RuntimeException("Stub!"); }//获取当前类的指定注解(包括父类的)
public <T extends Annotation> T getAnnotation(Class<T> annotationClass);//获取当前类的所有注解(包括父类的)
public Annotation getAnnotations();//获取当前类的所有指定注解(包括父类的)
public default <T extends Annotation> T[] getAnnotationsByType(Class<T> annotationClass) { throw new RuntimeException("Stub!"); }//获取当前类的指定注解
public default <T extends Annotation> T getDeclaredAnnotation(Class<T> annotationClass) { throw new RuntimeException("Stub!"); }//获取当前类的所有指定注解
//如果如果使用了元注解@Repeatable(Java1.8)的话,能实现同一个元素上有多个相同的注解
public default <T extends Annotation> T[] getDeclaredAnnotationsByType(Class<T> annotationClass) { throw new RuntimeException("Stub!"); }//获取当前类的所有注解
public Annotation getDeclaredAnnotations();
}
Class、Constructor、Method、Field这些类都实现了AnnotatedElement接口。其中Class是直接实现的,其他都是继承了AccessibleObject类(它实现了AnnotatedElement接口)。
所以基本上反射常用的类都具备获取注解的能。
结合反射与注解实现类似ButterKnife的功能
先康康Activity中的实现,@BindLayout负责setContentLayout,@BindView负责findViewById,@BindClick负责setOnClickListener。BindUtils.bind(this)是触发解析注解的方法。
@BindLayout(id = R.layout.activity_main)
class MainActivity : AppCompatActivity() {@BindView(id = R.id.btn)lateinit var btn: Button@BindView(id = R.id.btn2)lateinit var btn2: Buttonoverride fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)BindUtils.bind(this)btn.text = "我是1"btn2.text = "我是2"}@BindClick(ids = [R.id.btn, R.id.btn2])fun click(v: View){when (v.id){R.id.btn -> Toast.makeText(this, "我按了1", Toast.LENGTH_SHORT).show()R.id.btn2 -> Toast.makeText(this, "我按了2", Toast.LENGTH_SHORT).show()}}
}
在康康注解的实现
@Retention(RetentionPolicy.RUNTIME) //保留期是运行时
@Target(value = ElementType.TYPE) //目标为类
public @interface BindLayout {int id() default -1;
}@Retention(RetentionPolicy.RUNTIME)
@Target(value = ElementType.FIELD) //目标为属性
public @interface BindView {int id() default -1;
}@Retention(RetentionPolicy.RUNTIME)
@Target(value = ElementType.METHOD) //目标为方法
public @interface BindClick {int[] ids(); //接受id数组
}
最后康康解析注解的BindUtils,都是写反射的基操。
public class BindUtils {public static void bind(Activity activity){if (bindLayout(activity)){bindView(activity);bindOnClick(activity);}}private static boolean bindLayout(Activity activity){Class clazz = activity.getClass();if (clazz.isAnnotationPresent(BindLayout.class)){BindLayout bindLayout = (BindLayout) clazz.getAnnotation(BindLayout.class);if (bindLayout.id() == -1) return false;activity.setContentView(bindLayout.id());}return true;}private static void bindView(Activity activity){Class clazz = activity.getClass();Field[] fields = clazz.getDeclaredFields();for (Field f : fields){if (f.isAnnotationPresent(BindView.class)){try {BindView bindView = f.getAnnotation(BindView.class);if (bindView.id() == -1) continue;View view = activity.findViewById(bindView.id());if (view == null) continue;f.setAccessible(true);f.set(activity, view);} catch (Exception e) {e.printStackTrace();}}}}private static void bindOnClick(Activity activity){Class clazz = activity.getClass();Method[] methods = clazz.getDeclaredMethods();for (Method m : methods){if (m.isAnnotationPresent(BindClick.class)){BindClick mOnclick = m.getAnnotation(BindClick.class);int[] ids = mOnclick.ids();for (int i = 0; i < ids.length; i++){final View view = activity.findViewById(ids[i]);if(view == null) continue;view.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {try {m.setAccessible(true);m.invoke(activity, view);} catch (Exception e) {e.printStackTrace();}}});}}}}
}
反射处理注解的缺点
只能处理运行期注解;
效率不高,影响程序性能。
(通过反射注解方式绑定view,在view数量少的情况还好。但是在实际项目中一个Activity可能需要绑定十几二十个view,而且还可能有数十个这样的Activity。这样情况下使用反射是非常不明智的选择)
ButterKnife的核心不是用反射,而是用APT。
APT处理编译期注解
注解处理器(Annotation Processing Tool)简称APT。它是javac的一个工具。
作用:APT可以用来在编译时扫描和处理注解。
应用场景:在框架中广泛运用,比如:ButterKnife、EventBus、Dagger2、ARouter…
APT内容我单独整理了一个笔记
Java注解处理器学习记录(实现乞丐版ButterKnife)
Java注解学习记录(反射也能实现ButterKnife)相关推荐
- Java设计模式学习记录-解释器模式
前言 这次介绍另一个行为模式,解释器模式,都说解释器模式用的少,其实只是我们在日常的开发中用的少,但是一些开源框架中还是能见到它的影子,例如:spring的spEL表达式在解析时就用到了解释器模式,以 ...
- java注释的理解,java注解原理——记录一下自己的理解
最近因为系统可能要更换成java语言,于是每天都在拼命的研究java的相关知识和框架.之前学习注解的时候,没有太深入的去理解它,只是觉得标注一下挺好用,但是现在在学到spring aop的时候,突然发 ...
- Java设计模式学习记录-单例模式
前言 已经介绍和学习了两个创建型模式了,今天来学习一下另一个非常常见的创建型模式,单例模式. 单例模式也被称为单件模式(或单体模式),主要作用是控制某个类型的实例数量是一个,而且只有一个. 单例模式 ...
- java 注解学习_JAVA注解学习
什么是注解 ? [这个解释起来比较....不知道怎么解释,还是直接百度吧,不会咱就抄一抄嘛~~~] 定义一:用一个词就可以描述注解,那就是元数据,即一种描述数据的数据.所以,可以说注解就是源代码的元数 ...
- Java 注解学习笔记
转自:http://wanqiufeng.blog.51cto.com/409430/458883 一.什么是java注解 注解,顾名思义,注解,就是对某一事物进行添加注释说明,会存放一些信息,这些信 ...
- Java注解学习一:注解术语
2019独角兽企业重金招聘Python工程师标准>>> 一.元注解 @Target:表示该注解可以用于什么地方.可选的ElementType参数包括: TYPE:类,接口(包括注解类 ...
- Java SE 学习记录06
@学习记录 开始学习Java 遵从同学的指导,从Java se开始学习 黑马的JavaSE零基础入门 day06-01 面向对象 package day06;import java.util.Arra ...
- Java SE 学习记录——进阶版11
@学习记录 开始学习Java 遵从同学的指导,从Java se开始学习 黑马的JavaSE零基础入门[网络编程] 第一章 网络编程入门 1.1 软件结构 22-02 软件结构 1.2 网络通信协议 2 ...
- java之学习记录 5 - 1 - 模拟拉勾项目介绍与后台系统搭建
项目架构(此文章只供个人学习的记录) 1 项目介绍 拉勾教育后台管理系统,是提供给拉勾教育的相关业务人员使用的一个后台管理系统, 业务人员可以在这个后台管理系统中,对课程信息.讲师信息. 学员信息等数 ...
最新文章
- 【转载】“error LNK1169: 找到一个或多个多重定义的符号”的解决方法
- python用中文怎么说-震惊!!!python可以用中文写代码
- 微信小程序,前端大梦想(七)
- IE9:为什么 Acid3 无足轻重
- 协方差矩阵, 相关系数矩阵
- WKViewManager iOS 弹窗架构
- 畅游“私有化”完成,搜狐股价暴涨逾25%
- 基于JAVA+SpringMVC+Mybatis+MYSQL的高校科研管理系统
- linux开发板通过网线连接电脑
- cesium添加高德路网中文注记图及高德在线地图介绍
- java zip解压抛出异常,java – ZipFile抛出错误,但ZipInputStream能够解压缩归档
- 特网云 DirectAdmin 安装SSL
- list(map(tokenizer.tokenize, text))
- Parallel ScavengeGC收集器
- 齐岳2-苯基吡啶(C-N)|2-phenylpyridine|cas1008-89-5
- Unity2D游戏开发之保卫萝卜
- io密集服务器cpu性能,线程池中CPU密集型和IO密集型选择
- IDA 和 IDA-Python 学习笔记
- 微信小程序订单页面格式
- 海思Hi3516DV300软硬件全套开发资料