注解
作用和意义
- 注解又称标注,是Java1.5引入的,注解本身没有任务意义,单独的注解就是一种注释,它需要结合反射,插桩技术才能体验其用处
- 元注解: 注解上的注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.CLASS)
public @interface ARouter {
String path();
String group() default "";
}
- @Target 标识的是注解在什么上,比如class,方法,成员变量等
- @Retention 保留级别,有RUNTIME,CLASS,SOURCE 他们是包含关系,即CLASS包含SOURCE RUNTIME包含SOURCE和CLASS
- SOURCE: 1:APT保留级别是最好用(生成class之前) .2:IDE语法检查
- RUNTIME: 反射用(运行时)
反射
一般情况下,我们使用某个类时必定知道它是什么类,用来做什么的,并且能够获得这个类的引用,我们就可以对这个类进行实例化,然后使用它
反射则是一开始并不知道我要初始化的对象是什么,无法使用new来实例化对象,这个时候我们就可以使用jdk提供的反射调用. 就是在运行时,对于任意一个类,都能够知道这个类的属性和方法.对于任一对象,都能够调用它的任一方法,修改其属性.
下面示例(古老方法 自动findViewById)
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface InjectView {
@IdRes int value();
}
Activity activity;
反射获取成员变量Filed
Class clazz = Class.forName("android.app.Activity");
//获取自己的所有的成员变量,不包括父类
Field[] declaredFields = clazz.getDeclaredFields();
//获取自己和父类的所有的成员变量(不包括private),包括接口
Field[] fields = clazz.getFields();
//获取成员变量名为xxx的成员变量
Field xxxField = clazz.getField("xxx")
//判断xxxField上是否包含了InjectView注解
boolean isInjectView = xxxField.isAnnotationPresent(InjectView.class)
//如果存在这个注解则可以获取
if (isInjectView) {
//获取InjectView注解
InjectView injectView = declaredField.getAnnotation(InjectView.class);
//获取注解上的值
int id = injectView.value();
View view = activity.findViewById(id);
//开始设置值
xxxField.setAccessible(true);
xxxField.set(activity,view);
}
当我们对一个泛型类进行反射时,需要得到泛型中的真实数据类型来完成json反序列化的操作时,此时需要通过Type体系来完成.
Type接口包含了一个实现类(Class)和4个实现接口
- Class
- TypeVariable: 泛型类型变量
- GenericArrayType: 当描述的泛型是泛型类数组时 比如:List[], Map[]
- ParameterizedType: 具体泛型类型
- WildcardType:通配符泛型
示例(获取泛型里的具体对象)
class Response<T>{
Type mType;
public Response(){
ParameterizedType type = (ParameterizedType) getClass().getGenericSuperclass();
mType = type.getActualTypeArguments()[0];
}
public Type getType(){
return mType;
}
}
void test(){
//获取type类型:
//方法1:调用此方法会抛错异常,
Type type = new Response<String>().getType();
//方法2:可以获取到type为String.
Type type = new Response<String>(){}.getType();//new Response<String>(){}相当于创建了一个class文件.
//原因: 方法1是直接创建了一个对象,在编译成class之后不会保留泛型类型,全部为Object,而方法2是创建了一个匿名内部类,生成class时会保留原有的泛型签名
class Response2 extends Response<String>{}
//方法2等于是创建了下面Response2类式对象 也就是方法2 也可以写成下面这种写法
Type type = new Response2().getType();
}
泛型
泛型是JDK5引入了,JVM本身是不支持泛型,但是为了向下兼容,Java是一种伪泛型,所以在编译期的时候会对泛型进行擦除,Java在运行时是不存在泛型信息的.
JAVA中的泛型使用分为以下三种情况
//情况一 泛型接口
public interface ILogin<T>{}
//情况二 泛型类
public class Login<T>{}
//情况三 泛型方法
public <T> T getLoginBean(){
return new Login<T>();
}
泛型怎么擦除
- 检查泛型的类型,获取目标类型
- 擦除类型变量,并替换为限定的类型,这里又分三种情况
- 如果泛型类型的类型变量没有限定也就是只有一个,则最终这个T会变成Object
- 如果有限定比如 ,则会用XXXBean作为原始的类型
- 如果有多个限定的话比如 <T extends XBean&YBean>,则以第一个作为原始类型
- 生成桥接方法以在扩展保持多态(在有实现接口的时候会出现桥接方法,因为接口泛型里的泛型会转为Object,实现接口就得实现接口里的方法,所以会出现一个桥方法 )
- 泛型擦除后会将泛型信息保留在类的常量池中
泛型好处
- 代码更健壮,只要编译期没有报错,运行时就不会出现ClassCastException
- 代码更简洁,很多都不需要强转
- 代理更灵活,复用性高