- 声明: 本人最近也在学习自定义注解但是看着网上的代码拿下来运行都直接报错,我在想 你们在抄别人的帖子之前要自己先运行一遍,本人这边通过学习进行纠正他们自定义注解创建对象和查找id 的错误。
在进行学习自定义注解之前首先要了解反射,因为在你注解将对象传过来的时候那么你的当前对象要进行利用反射进行查找和赋值,然后如果看不懂那就先直接看最后自定义注解的解释就会明白为什么要先看反射了。
/**
* 包名加类名
*/
public String getName();
/**
* 类名
*/
public String getSimpleName();
/**
* 返回当前类和父类层次的public构造方法
*/
public Constructor<?>[] getConstructors();
/**
* 返回当前类所有的构造方法(public、private和protected)
* 不包括父类
*/
public Constructor<?>[] getDeclaredConstructors();
/**
* 返回当前类所有public的字段,包括父类
*/
public Field[] getFields();
/**
* 返回当前类所有申明的字段,即包括public、private和protected,
* 不包括父类
*/
public native Field[] getDeclaredFields();
/**
* 返回当前类所有public的方法,包括父类
*/
public Method[] getMethods();
/**
* 返回当前类所有的方法,即包括public、private和protected,
* 不包括父类
*/
public Method[] getDeclaredMethods();
/**
* 获取局部或匿名内部类在定义时所在的方法
*/
public Method getEnclosingMethod();
/**
* 获取当前类的包
*/
public Package getPackage();
/**
* 获取当前类的包名
*/
public String getPackageName$();
/**
* 获取当前类的直接超类的 Type
*/
public Type getGenericSuperclass();
/**
* 返回当前类直接实现的接口.不包含泛型参数信息
*/
public Class<?>[] getInterfaces();
/**
* 返回当前类的修饰符,public,private,protected
*/
public int getModifiers();
类里面每个属性对应一个对象Field,每个方法对应一个对象Method。
2.1.2.2、找到添加注解的属性或者方法
上面说道每个属性对应Field,每个方法对应Method。而且Field和Method都实现了AnnotatedElement接口。都有AnnotatedElement接了我们就可以很容易的找到添加了我们指定注解的方法或者属性了。
AnnotatedElement接口常用方法如下:
/**
* 指定类型的注释是否存在于此元素上
*/
default boolean isAnnotationPresent(Class<? extends Annotation> annotationClass) {
return getAnnotation(annotationClass) != null;
}
/**
* 返回该元素上存在的指定类型的注解
*/
<T extends Annotation> T getAnnotation(Class<T> annotationClass);
/**
* 返回该元素上存在的所有注解
*/
Annotation[] getAnnotations();
/**
* 返回该元素指定类型的注解
*/
default <T extends Annotation> T[] getAnnotationsByType(Class<T> annotationClass) {
return AnnotatedElements.getDirectOrIndirectAnnotationsByType(this, annotationClass);
}
/**
* 返回直接存在与该元素上的所有注释(父类里面的不算)
*/
default <T extends Annotation> T getDeclaredAnnotation(Class<T> annotationClass) {
Objects.requireNonNull(annotationClass);
// Loop over all directly-present annotations looking for a matching one
for (Annotation annotation : getDeclaredAnnotations()) {
if (annotationClass.equals(annotation.annotationType())) {
// More robust to do a dynamic cast at runtime instead
// of compile-time only.
return annotationClass.cast(annotation);
}
}
return null;
}
/**
* 返回直接存在该元素岸上某类型的注释
*/
default <T extends Annotation> T[] getDeclaredAnnotationsByType(Class<T> annotationClass) {
return AnnotatedElements.getDirectOrIndirectAnnotationsByType(this, annotationClass);
}
/**
* 返回直接存在与该元素上的所有注释
*/
Annotation[] getDeclaredAnnotations();
反射API 了解过后我们要了解要一下 JAVA 中自带的 4 中 反射定义,因为我们的注解的生命周期和注解的定义方式(成员位置定义或是方法定义等)还有注解是否可被继承都是在JAVA 提供的4 大注解基础上进行实现。
@Target | 表明注解可以出现的地方,其中有记住类型,通过枚举 ElementType.****(详情见下方) |
@Retention | 表明注解生成的时间详(详情见下方) |
@Document | 表明可以被javadoc此类的工具文档化(详情见下方) |
@Inherited | 表明是否允许子类继承该注解,默认为false(详情见下方) |
- 定义注解如何定义 通过 @interface 进行定义 比如 public @interface ViewById{} 那么我们这个就是注解类
- @Target 在定义的注解类上进行添加 Target中有几种类型
@Target-ElementType类型 | 说明------ 可在此类型中进行使用注解 |
ElementType.TYPE | 接口、类、枚举、注解 |
ElementType.FIELD | 字段、枚举的常量 |
ElementType.METHOD | 方法 |
ElementType.PARAMETER | 方法参数 |
ElementType.CONSTRUCTOR | 构造函数 |
ElementType.LOCAL_VARIABLE | 局部变量 |
ElementType.ANNOTATION_TYPE | 注解 |
ElementType.PACKAGE | 包 |
2.2@Retention
2.3@Document
@Document 类型 | 说明 |
@Document | 表明我们标记的注解可以被javadoc此类的工具文档化,xml格式的 |
2.4@Inherited
@Inherited 类型 | 说明 |
@Inherited | 表明我们标记的注解是被继承的。比如,如果一个父类使用了@Inherited修饰的注解,则允许子类继承该父类的注解。 |
以上是Java中提供的4个注解我们的注解就是以上注解的扩展,当然扩展要使用反射对于自定义注解的一个实现,不然自定义注解会无效果
重中之重接下来实现自定义注解并现实注解自动创建对象的功能附加butterknife中对View id 的查找与创建
3.1 声明注解
**
* Create by Jing
*/
//ElementType.FIELD 可对字段进行使用注解
//RetentionPolicy.RUNTIM 此生命周期注后保存包class文件并且jvm加载完class 仍然存在
//@interface 定义 此 Auto 是注解
(value = ElementType.FIELD)
(RetentionPolicy.RUNTIME)
@interface Auto {
}
3.2 使用反射进行对注解功能的实现
/**
* Create by Jing
*/
public class AutoProcess {
public static void bind( Activity object) {
if (object != null) {
//当创建的不为空的时候 获取当前的 class 通过getDeclaredFields 获取当前 不管private 还是public 的成员变量
Field[] fields = object.getClass().getDeclaredFields();
//遍历成员变量
for (final Field field : fields) {
//返回当前指定的 注解类型
Auto autoWiredAnnotation = field.getAnnotation(Auto.class);
//那么当前指定的注解类型不为空的情况下
if (autoWiredAnnotation != null) {
//如果是私有变量的情况下
field.setAccessible(true);
try {
//创建一个实例
Class<?> autoCreateClass = field.getType();
//返回当前父类所有层级Public 方法
Constructor autoCreateConstructor = autoCreateClass.getConstructor();
//把我们为空的那个对象进行 set值 创建对象
field.set(object, autoCreateConstructor.newInstance());
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
}
}
}
}
3.3 在MainActivity中进行使用
public class MainActivity extends AppCompatActivity {
Mess mMess;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
AutoProcess.bind(this);
//如何getName()方法报空指针那么我们的对象就创建失败 那么你就可以来找我了
String name = mMess.getName();
}
}
- 以上是自动创建对象那么我们来一个 凶一点的,butterknife 类型的注解
4.1 声明注解
/**
* Create by Assen
*/
(value={ElementType.FIELD})//FIELD表示的是成员变量级别可以使用该注解
(RetentionPolicy.RUNTIME)//RUNTIME级别可以被反射读取注解,都是运行时
public @interface ViewById {
int value();
}
4.2 使用反射给ViewByid 注解进行添加功能
public class ViewUtils {
public static void inject(Activity activity) {
// 1.获取所有的属性
Field[] fields = activity.getClass().getDeclaredFields();
// 2.过滤关于 ViewById 属性
for (Field field : fields) {
//获取注解封装---ViewById类型的注解。相当于对ViewById所标识属性的封装类
ViewById viewById = field.getAnnotation(ViewById.class);
if(viewById != null){
// 3.findViewById
View view = activity.findViewById(viewById.value());
field.setAccessible(true);
try {
// 4.反射注入
// activity 属性所在类,view 代表的是属性的值
field.set(activity,view);
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
}
}
4.3 使用
public class MainActivity extends AppCompatActivity {
Mess mMess;
//使用我们的注解进行初始化View
(R.id.sample_text)
private TextView mTextView;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
AutoProcess.bind(this);
ViewUtils.inject(this);
String name = mMess.getName();
mTextView.setText(name);
}
}