Java_注解 (元注解 / Java内置注解 / Android内置注解)
本文由 Luzhuo 编写,转发请保留该信息.
原文: http://blog.csdn.net/rozol/article/details/77758192
注解: 给程序看的提示. (注释: 给程序员看的提示)
Java1.5开始引入注解功能.
元数据: 描述数据的数据, 可以说注解便是这元数据. (除注解外, XML也被用于描述元数据)
注解仅仅是元数据, 与业务逻辑无关, 注解的 添加 / 删除 不能影响程序的执行
元注解
元注解
- @Retention(RetentionPolicy.RUNTIME) // @Retention指定注解的(生命周期)保留范围(默认CLASS) (SOURCE:在编译阶段丢弃 / CLASS:在类加载时丢弃(在字节码文件中有用) / RUNTIME:始终存在(可反射读取))
- @Target({ElementType.TYPE, ElementType.METHOD}) // @Target:指定注解被用于什么地方(默认用于任何地方) (TYPE: 类,接口,enum / FIELD: 字段 / METHOD: 方法 / PARAMETER: 参数 / CONSTRUCTOR: 构造 / LOCAL_VARIABLE: 局部变量 / ANNOTATION_TYPE: 注解 / PACKAGE: 包 / TYPE_PARAMETER: 类型参数 / TYPE_USE: 类型使用)
- @Documented // 将被 javadoc 工具提取成文档
- @Inherited // 被该注解修饰的类(在类上声明才有效)具有继承性 (父类使用@Inherited修饰类, 其子类自动具有该注解)
- @Repeatable(ArrayAnnotation.class) // 可重复的注解 (使用该注解还需创建 @ArrayAnnotation 注解) (使用: @JavaAnnotations())
/**
* Java的元注解
* Java1.8的 @Target 注解引入了 TYPE_PARAMETER 和 TYPE_USE
* Java1.8引入了 @Repeatable 注解
* @author Luzhuo
*/
@Retention(RetentionPolicy.RUNTIME) // @Retention指定注解的(生命周期)保留范围(默认CLASS) (SOURCE:在编译阶段丢弃 / CLASS:在类加载时丢弃(在字节码文件中有用) / RUNTIME:始终存在(可反射读取))
@Target({ElementType.TYPE, ElementType.METHOD}) // @Target:指定注解被用于什么地方(默认用于任何地方) (TYPE: 类,接口,enum / FIELD: 字段 / METHOD: 方法 / PARAMETER: 参数 / CONSTRUCTOR: 构造 / LOCAL_VARIABLE: 局部变量 / ANNOTATION_TYPE: 注解 / PACKAGE: 包 / TYPE_PARAMETER: 类型参数 / TYPE_USE: 类型使用)
@Documented // 将被 javadoc 工具提取成文档
@Inherited // 被该注解修饰的类(在类上声明才有效)具有继承性 (父类使用@Inherited修饰类, 其子类自动具有该注解)
@Repeatable(ArrayAnnotation.class) // 可重复的注解 (使用该注解还需创建 @ArrayAnnotation 注解) (使用: @JavaAnnotations())
public @interface MetaAnnotations { // @interface关键字来定义注解
// 只有的数据类型有: 基本数据类型 / String / Class / enum / Annotation / <- 这些数据类型对应的一位数组
public String name() default "JavaAnnotations"; // default 设置默认值 (没写默认值,使用时编译器会要求加上该属性) (默认值不能为null, 可用 -1 或 "" 表示该元素不存在)
public int number() default 100;
public String value() default ""; // 当注解只包含一个名为 value 的属性时, 该注解在被使用时可忽略属性名直接写值
public enum Color{RED, YELLOW, BULE};
public Color color() default Color.BULE;
}
/**
* 当 MetaAnnotations 注解使用了 @Repeatable 可重复注解后, 还需创建一个 ArrayAnnotation 注解, 用于存放 MetaAnnotations 注解.
* ArrayAnnotation 使用的元注解必须和 MetaAnnotations 一样
* @author Luzhuo
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
@Documented
@Inherited
@interface ArrayAnnotation {
public MetaAnnotations[] value();
}
注解及其属性获取
注解 通过反射获取的类 / 方法 / 字段 上获取
public class Test {
public static void main(String[] args) throws Exception {
Class<?> clazz = Bean.class;
// 获取类的注解
classAnnotations(clazz);
// 获取方法的注解
methodAnnotations(clazz);
}
/**
* 获取类的注解
*/
private static void classAnnotations(Class<?> clazz) {
Annotation[] annotations = clazz.getAnnotations(); // 获取所有注解(含父类)
Annotation[] allAnnotations = clazz.getDeclaredAnnotations(); // 返回本类的所有注解(不含父类)
MetaAnnotations metaAnnotations = clazz.getAnnotation(MetaAnnotations.class); // 获取指定的注解, 不存在null
boolean exist = clazz.isAnnotationPresent(MetaAnnotations.class); // 是否存在该注解
System.out.println("Class annotations[]: " + Arrays.toString(annotations));
System.out.println("Class allAnnotations[]: " + Arrays.toString(allAnnotations));
System.out.println("Class metaAnnotations: " + metaAnnotations);
System.out.println("Class exist: " + exist);
}
/**
* 获取方法的注解
*/
private static void methodAnnotations(Class<?> clazz) throws Exception {
Method method = clazz.getMethod("show");
Annotation[] annotations = method.getAnnotations();
Annotation[] allAnnotations = method.getDeclaredAnnotations();
// 注意: 当拥有多条相同的注释时, 使用的是 ArrayAnnotation 而非 MetaAnnotations .
ArrayAnnotation arrayAnnotation = method.getAnnotation(ArrayAnnotation.class);
boolean exist = method.isAnnotationPresent(ArrayAnnotation.class);
// Java8新增的方法
MetaAnnotations[] annotations_new = method.getAnnotationsByType(MetaAnnotations.class);
MetaAnnotations[] allAnnotations_new = method.getDeclaredAnnotationsByType(MetaAnnotations.class);
System.out.println("Method annotations[]: " + Arrays.toString(annotations));
System.out.println("Method allAnnotations[]: " + Arrays.toString(allAnnotations));
System.out.println("Method arrayAnnotation: " + arrayAnnotation);
System.out.println("Method exist: " + exist);
System.out.println("Method new annotations[]: " + Arrays.toString(annotations_new));
System.out.println("Method new allAnnotations[]: " + Arrays.toString(allAnnotations_new));
}
}
/**
* 使用注解
* @author Luzhuo
*/
@MetaAnnotations(name = "Test", value = "abc")
class Bean {
@MetaAnnotations(color = Color.RED)
@MetaAnnotations(number = 10)
public void show(){}
}
Java8新注解详解
@Target 注解引入了 TYPE_PARAMETER 和 TYPE_USE
@Repeatable 注解
/**
* Java1.8新增的注解专讲
* Java1.8的 @Target 注解引入了 TYPE_PARAMETER 和 TYPE_USE
* Java1.8引入了 @Repeatable 注解
* @author Luzhuo
*/
public class Java8NewAnnotations {
public static void main(String[] args) throws Exception {
Class<?> clazz = new Bean2<Integer>().getClass();
// @Target 注解的 ElementType.TYPE_PARAMETER
// Bean<T> 泛型 #TODO
// =================================================
// @Target 注解的 ElementType.TYPE_USE
// === 类 ===
Targe_TYPE_USE classTypeUse = clazz.getAnnotation(Targe_TYPE_USE.class);
System.out.println("classTypeUse: " + classTypeUse);
// === 接口 ===
AnnotatedType[] annoInterfaces = clazz.getAnnotatedInterfaces(); // 获取注解接口 Java1.8
for (AnnotatedType annoType : annoInterfaces){
Type type = annoType.getType();
Annotation[] interfaceAnnotations = annoType.getAnnotations();
System.out.println("接口类型: " + type.toString() + " 接口的注解: " + Arrays.toString(interfaceAnnotations));
}
// === 字段 ===
Field[] fields = clazz.getFields();
// list 泛型 #TODO
Field list = fields[0];
// name
Field name = fields[1];
// 1. 字段类型
Type nameType = name.getType();
AnnotatedType stringType = name.getAnnotatedType(); // 获取 name 注解类型 String Java1.8
Annotation[] stringAnnotations = stringType.getAnnotations(); // 获取 String 的注解
System.out.println("name字段类型: " + nameType.toString() + " 字段的注解: " + Arrays.toString(stringAnnotations));
// 2. 类型强转 #TODO
// file #TODO
Field file = fields[2];
// === 方法 ===
Method getName = clazz.getMethod("getName", String.class);
// 返回
AnnotatedType returnType = getName.getAnnotatedReturnType(); // Java1.8
Annotation[] returnAnnotaions = returnType.getAnnotations();
System.out.println("返回类型: " + returnType.getType().toString() + " 返回注解: " + Arrays.toString(returnAnnotaions));
// 参数
AnnotatedType[] parameters = getName.getAnnotatedParameterTypes();
for (AnnotatedType parameter : parameters){
Type type = parameter.getType();
Annotation[] parameterAnnotations = parameter.getAnnotations();
System.out.println("参数类型: " + type.toString() + " 参数注解: " + Arrays.toString(parameterAnnotations));
}
// 异常
AnnotatedType[] exceptions = getName.getAnnotatedExceptionTypes();
for (AnnotatedType excep : exceptions){
Type type = excep.getType();
Annotation[] exceptionAnnotations = excep.getAnnotations();
System.out.println("异常类型: " + type.toString() + " 异常注解: " + Arrays.toString(exceptionAnnotations));
}
// =================================================
// @Repeatable √
Annotation[] annotations_repeatable = clazz.getAnnotationsByType(Targe_Repeatable.class);
System.out.println("@Repeatable: " + Arrays.toString(annotations_repeatable));
}
}
// === 使用注解 ==================================
// @Repeatable
@Targe_Repeatable("abc√")
@Targe_Repeatable("123√")
@Targe_TYPE_USE("类√")
// @Target - TYPE_PARAMETER
class Bean2<@Targe_TYPE_PARAMETER("泛型") T> implements @Targe_TYPE_USE("接口√") Serializable{
// @Targe - TYPE_USE
public List<@Targe_TYPE_USE("泛型") String> list = new ArrayList<>();
public @Targe_TYPE_USE("字段类型√") String name = (@Targe_TYPE_USE("类型强转") String) "Bean";
public File file = new java.io.@Targe_TYPE_USE("创建对象") File("filename");
public @Targe_TYPE_USE("返回√") String getName(@Targe_TYPE_USE("参数√") String name) throws @Targe_TYPE_USE("抛异常√") Exception{
return name;
}
}
// === 定义注解 ==================================
/**
* @Target 注解的 ElementType.TYPE_PARAMETER
* 类型参数: 用来标注 类型参数<T>.
* @author Luzhuo
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE_PARAMETER)
@interface Targe_TYPE_PARAMETER {
public String value() default "TYPE_PARAMETER";
}
/**
* @Target 注解的 ElementType.TYPE_USE
* 类型使用: 只要是 类型 ,都可以进行注解.
* @author Luzhuo
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE_USE)
@interface Targe_TYPE_USE {
public String value() default "TYPE_USE";
}
/**
* @Repeatable 注解
* 可重复的注解
* @author Luzhuo
*/
@Retention(RetentionPolicy.RUNTIME)
@Repeatable(ArrayRepeatable.class)
@interface Targe_Repeatable {
public String value() default "Repeatable";
}
@Retention(RetentionPolicy.RUNTIME)
@interface ArrayRepeatable {
public Targe_Repeatable[] value();
}
Java内置注解
Override(复写父类方法) / Deprecated(不建议使用) / SuppressWarnnings(抑制编译器警告)
/**
* Java内置的注解
* Override(复写父类方法) / Deprecated(不建议使用) / SuppressWarnnings(抑制编译器警告)
* @author Luzhuo
*/
public class JavaAnnotations extends Annotation {
/**
* 告知编译器忽略指定警告信息
*/
@SuppressWarnings("rawtypes")
public List list = new ArrayList();
public JavaAnnotations(Object value) {
super(value);
}
/**
* 复写父类的方法
*/
@Override
public Object getValue() {
return super.getValue();
}
/**
* 告知编译器不建议使用
*/
@Deprecated
public String getName() {
return JavaAnnotations.class.getSimpleName();
}
/**
* 告知编译器, 程序员写的正文代码不会对其 可变参数 执行潜在的不安全操作
* Type safety: Potential heap pollution via varargs parameter list. (潜在的可变参数列表堆污染)
* Java 1.7
*/
@SafeVarargs
public static void aaa(List<String>... list){
// 把类型弄乱套, 编译器并不会报错
Object[] array = list; // 参数被重新构建元素类型, 他的类型包括 Object / String.
array[0] = 123;
}
/**
* 函数式接口, 主要用于Lambda表达式
* 只有一个抽象方法的普通接口 (多个 default / static 方法不受影响)
* @author Luzhuo
*/
@FunctionalInterface
public interface RunnableTest {
// 只能有一个抽象方法
public abstract void runTest(String id);
// 其他的, 多个不影响
public default String getName(){
return RunnableTest.class.getSimpleName();
}
public static int getId(){
return 0x007;
}
}
public static void main(String[] args) {
// Lambda表达式
RunnableTest runnable = id -> System.out.println("RunnableTest: " + id);
runnable.runTest("123");
// 其他方法的使用
System.out.println("Name: " + runnable.getName());
System.out.println("Id: " + RunnableTest.getId());
}
}
Android内置注解
- Nullness, 参数 或 返回值 为 null 或 非null
- 资源类注解: @StringRes (R.string.xxx), @DrawableRes (R.mipmap.xxx), @ColorRes (R.color.xxx),@LayoutRes(R.layout.xxx), @AnyRes(任意资源), @AnimatorRes, @AnimRes, @FranctionRes, @InterpolatorRes, @XmlRes, @ArrayRes, @AttrRes, @BoolRes, @IntegerRes, StringRes, @IdRes, @RawRes, @MenuRes, @StyleableRes @StyleRes
- 线程相关注解: @UiThread, @MainThread, @WorkerThread, @BinderThread
- 参数类型 及其 范围 相关注解: @Size(数组 / 集合 / 字符串 的大小) / @IntRange / @FloatRange
- 权限注解: @RequiresPermission
- @CheckResult 建议对返回值做些操作
- @CallSuper 方法重写后要调用父类方法
Nullness
Nullness, 参数 或 返回值 为 null 或 非null
注解使用
/**
* Nullness, 参数 或 返回值 为 null 或 非null
*/
public class NullnessAnnotations {
/**
* 参数不能为null
*/
public void print1(@NonNull String id){
System.out.println(id);
}
/**
* 参数可以为null
*/
public void print2(@Nullable Integer id){
System.out.println(id);
}
/**
* 返回值不能为null
*/
@NonNull
public String getName1(){
return null; // 'null' is returned by the method declared as @NotNull
}
/**
* 返回值可以为null
*/
@Nullable
public String getName2(){
return null;
}
}
测试
// Nullness 是否为空注解
NullnessAnnotations nullnessTest = new NullnessAnnotations();
nullnessTest.print1(null); // Passing 'null' argument to parameter annotated as @NotNull
nullnessTest.print2(null);
nullnessTest.getName1();
nullnessTest.getName2();
资源类型注解, 限制参数的资源类型
@StringRes (R.string.xxx), @DrawableRes (R.mipmap.xxx), @ColorRes (R.color.xxx), @LayoutRes(R.layout.xxx), @AnyRes(任意资源)
@AnimatorRes, @AnimRes, @FranctionRes, @InterpolatorRes, @XmlRes
@ArrayRes, @AttrRes, @BoolRes, @IntegerRes, StringRes, @IdRes, @RawRes
@MenuRes, @StyleableRes @StyleRes
注解使用
/**
* 资源类型注解, 限制参数的资源类型
* @StringRes (R.string.xxx), @DrawableRes (R.mipmap.xxx), @ColorRes (R.color.xxx), @LayoutRes(R.layout.xxx), @AnyRes(任意资源)
* @AnimatorRes, @AnimRes, @FranctionRes, @InterpolatorRes, @XmlRes
* @ArrayRes, @AttrRes, @BoolRes, @IntegerRes, StringRes, @IdRes, @RawRes
* @MenuRes, @StyleableRes @StyleRes
*/
public class ResTypeAnnotations {
/**
* String类型 资源
*/
public void setStringRes(@StringRes int id){
System.out.println(id);
}
/**
* Drawable类型 资源
*/
public void setImageRes(@DrawableRes int id){
System.out.println(id);
}
/**
* Color类型 资源
*/
public void setColorRes(@ColorRes int id){
System.out.println(id);
}
/**
* Interpolator类型(xml插值器文件) 资源
*/
public void setInterPolatorRes(@InterpolatorRes int id){
System.out.println(id);
}
// ...
/**
* 任何资源
*/
public void setAnyRes(@AnyRes int id){
System.out.println(id);
}
}
测试
// Res 资源类型注解
ResTypeAnnotations resIdTest = new ResTypeAnnotations();
resIdTest.setStringRes(123456); // Expected resource of type string
resIdTest.setStringRes(R.color.colorAccent); // Expected resource of type string
resIdTest.setStringRes(R.string.app_name);
resIdTest.setImageRes(R.mipmap.ic_launcher);
resIdTest.setColorRes(R.color.colorAccent);
resIdTest.setAnyRes(123456);
resIdTest.setAnyRes(R.color.colorAccent);
线程相关注解
线程相关注解: @UiThread, @MainThread, @WorkerThread, @BinderThread
注解使用
/**
* 线程相关注解
* @UiThread, @MainThread, @WorkerThread, @BinderThread
*/
public class ThreadAnnotaions {
/**
* UI线程, 标记该方法运行于UI线程, 也就是Activity运行的窗口
* 一个应用可能有多个UI线程
*/
@UiThread
public void runUI(){
System.out.println("UiThread");
}
/**
* 主线程, 标记该方法运行于主线程
* 主线程也是UI线程, 于UiThread差别不大, 一般用UiThread标记
*/
@MainThread
public void runMain(){
System.out.println("MainThread");
}
/**
* 辅助线程, 标记该方法运行于子线程
*/
@WorkerThread
public void runWorker(){
System.out.println("WorkerThread");
}
/**
* Binder线程, 标记该方法运行于Binder线程池
*/
@BinderThread
public void runBinder(){
System.out.println("BinderPoolThread");
}
}
测试
// 线程相关注解
final ThreadAnnotaions threadAnnotaions = new ThreadAnnotaions();
threadAnnotaions.runUI();
threadAnnotaions.runMain();
threadAnnotaions.runWorker(); // Method runWorker must be called from the worker thread, currently inferred thread is main
threadAnnotaions.runBinder(); // Method runBinder must be called from the binder thread, currently inferred thread is main
new Thread(new Runnable() {
@Override
public void run() {
// 子线程
threadAnnotaions.runUI(); // TODO UIThread未被编译器警告提示
threadAnnotaions.runMain(); // TODO MainThread未被编译器警告提示
threadAnnotaions.runWorker();
threadAnnotaions.runBinder();
}
}).start();
参数类型 及其 范围 相关注解
@Size(数组 / 集合 / 字符串 的大小) / @IntRange / @FloatRange
注解使用
/**
* 参数类型 及其 范围 相关注解
* 当方法传入的参数值在一定的范围时, 可防止用户传入非期望的值
* @Size(数组 / 集合 / 字符串 的大小) / @IntRange / @FloatRange
*/
public class RangeAnnotations {
// @Size(数组 / 集合 / 字符串 的大小)
/**
* 数组 / 集合 / 字符串 的大小最小为3
*/
public void setArrayMin(@Size(min = 3) int[] ints){
System.out.println(Arrays.toString(ints));
}
/**
* 大小最大为5
*/
public void setArrayMax(@Size(max = 5) int[] ints){
System.out.println(Arrays.toString(ints));
}
/**
* 大小限制为2
*/
public void setArray(@Size(2) int[] ints){
System.out.println(Arrays.toString(ints));
}
/**
* 大小限制为2的倍数
*/
public void setArrayMul(@Size(multiple=2) int[] ints){
System.out.println(Arrays.toString(ints));
}
// @IntRange / @FloatRange 限制数字类型, 及其范围
/**
* 限制参数为整数类型 (参数可为: int long)
* 并且可限制其范围
*/
public void setInt(@IntRange(from = 1, to = 3) int number){
System.out.println(number);
}
/**
* 限制参数类型为浮点数类型 (参数可为: float double)
* 并且可限制其范围
*/
public void setFloat(@FloatRange(from = 0.0, to = 1.0) float number){
System.out.println(number);
}
}
测试
// 参数值范围相关注解
RangeAnnotations rangeAnnotations = new RangeAnnotations();
rangeAnnotations.setArrayMin(new int[]{1}); // Size must be at least 3 (was 1)
rangeAnnotations.setArrayMin(new int[]{1, 2, 3});
rangeAnnotations.setArrayMax(new int[]{1, 2, 3, 4, 5});
rangeAnnotations.setArray(new int[]{1, 2});
rangeAnnotations.setArrayMul(new int[]{1, 2});
// @IntRange / @FloatRange 限制数字类型, 及其范围
rangeAnnotations.setInt(1);
rangeAnnotations.setFloat(0.1f);
权限注解
@RequiresPermission 权限注解
注解使用
/**
* @RequiresPermission 权限注解
*/
public class PermissionAnnotations {
/**
* 字段的单个权限, 直接写在常量字段上
*/
@RequiresPermission(android.Manifest.permission.BLUETOOTH)
public static final String ACTION_REQUEST_DISCOVERABLE = "android.bluetooth.adapter.action.REQUEST_DISCOVERABLE";
/**
* 字段的读写个权限
*/
@RequiresPermission.Read(@RequiresPermission("me.luzhuo.permission.READ_PROVIDER"))
@RequiresPermission.Write(@RequiresPermission("me.luzhuo.permission.WRITE_PROVIDER"))
public static final Uri PersonUri = Uri.parse("content://me.luzhuo.person_provider/person");
/**
* 需要指定的注解
*/
@RequiresPermission(Manifest.permission.READ_EXTERNAL_STORAGE)
public void readFile(){
System.out.println("readFile");
}
/**
* 至少需要权限集中的一个
*/
@RequiresPermission(anyOf = {Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE})
public void writeFile(){
System.out.println("writeFile");
}
/**
* 需要权限集里的全部权限
*/
@RequiresPermission(allOf = {Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE})
public void readAndWriteFile(){
System.out.println("readAndWriteFile");
}
}
测试
// @RequiresPermission
PermissionAnnotations permissionAnnotations = new PermissionAnnotations();
permissionAnnotations.readFile(); // Call requires permission which may be rejected by user: code should explicitly check to see if permission is available (with `checkPermission`) or explicitly handle a potential `SecurityException`
if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) {
// 如果检查权限通过
permissionAnnotations.writeFile();
}
if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED &&
ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) {
permissionAnnotations.readAndWriteFile();
}
if (ContextCompat.checkSelfPermission(this, "me.luzhuo.permission.READ_PROVIDER") == PackageManager.PERMISSION_GRANTED &&
ContextCompat.checkSelfPermission(this, "me.luzhuo.permission.WRITE_PROVIDER") == PackageManager.PERMISSION_GRANTED) {
getContentResolver().delete(PermissionAnnotations.PersonUri, "`_id = ?", new String[]{"21"});
}
@CheckResult 建议对返回值做些操作
@CheckResult
注解使用
/**
* @CheckResult 建议对返回值做些操作
*/
public class CheckResultAnnotations {
/**
* 对返回的结果, 建议调用 print(int) 操作
*/
@CheckResult(suggest = "#print(int)")
public CheckResultAnnotations getInstance(){
return this;
}
public void print(int number){
System.out.println(number);
}
}
测试
// @CheckResult 建议对返回值做些操作
CheckResultAnnotations checkResultAnnotations = new CheckResultAnnotations();
checkResultAnnotations.getInstance(); // The result of 'getInstance' is not used; did you mean to call 'print(int)'?
checkResultAnnotations.getInstance().print(1);
CallSuper 方法重写后要调用父类方法
@CallSuper
注解使用
/**
* CallSuper 方法重写后要调用父类方法
* 当该方法被复写时, 期望在复写后也要 调用该方法
*/
public class CallSuperAnnotations {
/**
* 方法复写时, 期望子类复写该方法后也要被 调用
*/
@CallSuper
public void print(){
System.out.println(CallSuperAnnotations.class.getSimpleName());
}
}
测试
/**
* 子类
*/
class ChildCallSuperAnnotations extends CallSuperAnnotations{
@Override
public void print() {
// 若不写, 则提示: Overriding method should call super.print
super.print();
}
}
其他
@VisibleForTesting 可标注到类 方法 字段上, 是否对测试可见
@Keep 保持代码,拒绝混淆
注解使用
/**
* 其他注解
*
* @VisibleForTesting 可标注到类 方法 字段上, 是否对测试可见
* otherwise: PRIVATE(private)(默认) / PACKAGE_PRIVATE(default) / PROTECTED(protected) / NONE
*
* @Keep 保持代码,拒绝混淆
* 该注解标注到 类 / 方法 上时, 不会被混淆
*/
public class OtherAnnotations {
/**
* @VisibleForTesting 可标注到类 方法 字段上, 是否对测试可见
*/
@VisibleForTesting
public static class VisibleForTestingAnnotations {
@VisibleForTesting
public int number = 100;
/**
* private 方法 为什么修饰符是 public, 因为测试无法访问私有方法, 告知这个 public 权限是给测试可见的, 原先权限是 private
* 这个注解加不加都一样, 没什么卵用, 反正就是告诉你权限这回事儿, 并不会对权限造成任何影响
*/
@VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
public String getName(){
return VisibleForTestingAnnotations.class.getSimpleName();
}
}
/**
* @Keep 保持代码,拒绝混淆
*/
@Keep
public static class KeepAnnotations{
@Keep
public String getName(){
return KeepAnnotations.class.getSimpleName();
}
}
}