简介
注解(Annotation),也叫元数据。一种代码级别的说明。它是JDK1.5及以后版本引入的一个特性,与类、接口、枚举是在同一个层次。它可以声明在包、类、字段、方法、局部变量、方法参数等的前面,用来对这些元素进行说明。善用注解可提高您的代码质量和效率。
JDK自带的注解
@Override-----------------------表示当前方法覆盖了父类的方法
@Deprecated-------------------表示方法已经过时,方法上有横线,使用时会有警告。
@SuppressWarnings-------表示关闭一些警告信息(通知java编译器忽略特定的编译警告)
下面我们进行一个示例来演示这3种注解的使用
定义一个Animal的抽象类,包括3个方法,其中eat()方法设置为过时的,方法头加上@Deprecated:
public abstract class Animal {
privatestatic final String TAG = Animal.class.getSimpleName();
/**
* eat
*@deprecated Use {@link #feed()}
*/
@Deprecated
public void eat() {
feed();
}
/**
* feed
*/
public void feed() {
Log.e(TAG, "feed");
}
/**
* sleep
*/
public voids leep() {
Log.e(TAG, "sleep");
}
}
新建一个Pig的类,使继承于Animal,因为它重写了sleep()方法,所以在其方法上加上@Override:
public class Pig extends Animal {
privatestatic final String TAG = Pig.class.getSimpleName();
@Override
public voidsleep() {
Log.e(TAG, "sleep");
}
}
假设,我们在一个叫test()的方法中实例化一个Pig对象,然后分别调用它的eat()、feed()和sleep()方法:
private void test() {
Animal pig= new Pig();
pig.eat();
pig.feed();
pig.sleep();
}
可以在你的IDE中看到调用pig.eat();的代码是被加上了划线的,这是一个代码的警告,因为我们之前把eat()方法设置为过时的,若此时想不显示此警告,可以在test()方法上加入@SuppressWarnings("deprecation"),如:
@SuppressWarnings("deprecation")
private void test() {
Animal pig= new Pig();
pig.eat();
pig.feed();
pig.sleep();
}
这里说明一下,@SuppressWarnings注解接收一个字符串参数,其参数含义是:
@SuppressWarnings("unchecked")--------------未检查的转化,如集合没有指定类型
@SuppressWarnings("unused")--------------------未使用的变量
@SuppressWarnings("resource")------------------有泛型未指定类型
@SuppressWarnings("path")-------------------------在类路径,原文件路径中有不存在的路径
@SuppressWarnings("deprecation")------------使用了某些不赞成使用的类和方法
@SuppressWarnings("fallthrough")--------------switch语句执行到底没有break关键字
@SuppressWarnings("serial")------------------------某类实现Serializable 但是没有定义serialVersionUID这个需要但是不必须的字段
@SuppressWarnings("rawtypes")-----------------没有传递带有泛型的参数
@SuppressWarnings("all") ----------------------------全部类型的警告
Android中的注解
除了Java JDK自带的3种注解外,Android中也有相应类型的注解,分别是Nullness注解、资源注解、线程注解、值约束注解、权限注解、返回值注解、CallSuper注解、Typedef 注解、代码可访问性注解,等。下面看看这些注解的是如何使用的
Nullness注解
Nullness注解有:@Nullable 和@NonNull。它们是用于检查给定变量、参数或返回值是否为null的情况。@Nullable指示可以为null,而@NonNull则指示不可为null。
示例:
public class Person {
@NonNull
privateString mName;
@Nullable
publicString getName() {
returnmName;
}
public voidsetName(@NonNull String name) {
mName =name;
}
}
在Person类中全局变量mName和方法setName()的参数都标注为不可为空,而getName()方法标注为返回参数可为空,接着再来看看错误的调用代码:
@NonNull
private String mMyName;
……
Person person = new Person();
// 下面三行代码是一个错误的演示,都会被IDE给出相应的警告
mMyName = null;
person.setName(null);
mMyName = person.getName();
资源注解
资源注解有:@StringRes、@DrawableRes、@DimenRes、@ColorRes、@InterpolatorRes、@LayoutRes。等。因为 Android 对资源的引用以整型形式传递,所以它们的作用是用于检查参数资源类型是否合法。其中,如果您的参数支持多种资源类型,您可以在给定参数上添加多个注解。使用@AnyRes能够指示注解的参数可为任意类型的R资源。
示例:
void setColor(@ColorRes int color) {
// ...
}
调用:
setColor(R.string.app_name); // 错误的调用
setColor(R.color.colorWhite); // 正确的调用
在使用@ColorRes指定参数应为颜色资源后,若颜色整型(RRGGBB 或 AARRGGBB 格式)仍然是无法识别为颜色资源。这种情况,请改用 @ColorInt 注解指示参数必须为颜色整型。
示例:
void setColor2(@ColorInt int color) {
// ...
}
调用:
setColor2(R.color.colorWhite); // 错误的调用
setColor2(0x80F6F6F6); // 正确的调用
线程注解
线程注解有:@MainThread、@UiThread、@WorkerThread、@BinderThread和@AnyThread。它们用于检查某个方法是否从特定类型的线程调用。其中,构建工具会将 @MainThread 和 @UiThread 注解视为可互换,因此,您可以从 @MainThread 方法调用 @UiThread 方法,反之亦然。
值约束注解
值约束注解有:@IntRange、@FloatRange 和@Size 。它们用于验证传递的参数的值的取值范围或长度。
示例:
public void setAlpha(@IntRange(from=0, to=255) intalpha) {
// ...
}
public void setAlpha(@FloatRange(from=0.0, to=1.0)float alpha) {
// ...
}
public void setLocation(@Size(min=1) int[]location) {
// ...
}
public void setLocation2(@Size(max=2) int[]location){
// ...
}
public void setLocation3(@Size(3) int[]location) {
// ...
}
这里说下@Size注解,它是可以检查集合或数组的大小,以及字符串的长度,上面示例中,setLocationset的传入参数指定数组要求最小大小为1;而setLocationset2则指定要求最大大小为2;最后setLocation3是指定要求确切大小为3。
权限注解
使用@RequiresPermission注解可以验证方法调用方的权限。要检查有效权限列表中是否存在某个权限,请使用anyOf 属性。要检查是否存在一组权限,请使用allOf 属性。
示例1,若要调用setWallpaper()方法,要确保方法的调用方拥有permission.SET_WALLPAPERS权限:
@RequiresPermission(Manifest.permission.SET_WALLPAPER)
public abstract void setWallpaper(Bitmap bitmap)throws IOException;
示例2,若要调用copyFile()方法,要确保调用方同时具有外部存储空间的读写权限:
@RequiresPermission(allOf = {
Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.WRITE_EXTERNAL_STORAGE})
public static final void copyFile(String dest, Stringsource) {
...
}
示例3,intent权限的检查:
public void startMyActivity(@RequiresPermissionIntent intent) {
startActivity(intent);
}
调用:
Intent intent = new Intent(Intent.ACTION_CALL);
intent.setData(Uri.parse("tel:123456789"));
startMyActivity(intent);
这时,若调用方没有打电话权限,则IDE会有警告:Missing permissions required by intentIntent.ACTION_CALL:android.permsiion.CALL_PHONE
示例4,若需要单独读写权限的内容提供程序的权限,可使用:@RequiresPermission.Read或@RequiresPermission.Write注解指写权限要求:
@RequiresPermission.Read(@RequiresPermission(READ_HISTORY_BOOKMARKS))
@RequiresPermission.Write(@RequiresPermission(WRITE_HISTORY_BOOKMARKS))
public static final Uri BOOKMARKS_URI =Uri.parse("content://browser/bookmarks");
返回值注解
返回值注解:@CheckResult可以验证实际使用的是方法的结果还是返回值。该注解意味着需要调用方对方法的返回值进行处理
示例:
@CheckResult
public boolean checkValid(String value) {
returnTextUtils.isEmpty(value);
}
调用:
checkValid(""); // 错误的调用,因为调用方法后返回的值没有对其作处理
boolean check = checkValid(""); // 正确的调用
CallSuper注解
@CallSuper注解可以验证子类中重写父类的方法一定需要同时调用父类实现,即要求子类调用super.XX()。
示例:
// 父类代码:
public abstract class Animal {
@CallSuper
public voidsleep() {
}
}
// 子类代码,若不调用super.sleep();,则IDE会给出警告:Overriding method should callsuper.sleep
public class Pig extends Animal {
@Override
public voidsleep() {
// super.sleep();
}
}
Typedef 注解
Typedef注解,有些人会叫它枚举注解,因为它的作用跟枚举类似。Typedef 注解可以确保特定参数、返回值或字段引用特定的常量集。它们还可以完成代码以自动提供允许的常量。
Typedef 注解使用@interface声明新的枚举注解类型。@IntDef或@StringDef注解以及@Retention可以标注新注解,并且为定义枚举的类型所必需。@Retention(RetentionPolicy.SOURCE) 注解可以告知编译器不将枚举的注解数据存储在.class文件中。
示例1:
public static final int LENGTH_LONG = 1;
public static final int LENGTH_SHORT = 0;
@IntDef({LENGTH_SHORT, LENGTH_LONG})
@Retention(RetentionPolicy.SOURCE)
public @interface Duration {}
public void setDuration(@Duration int duration) {
//mDuration = duration;
}
@Duration
public int getDuration() {
returnLENGTH_LONG;
}
上面示例,其实是Toast的部分源码,我们在设置Toast显示时间时,只能设置两个值:LENGTH_LONG 和 LENGTH_SHORT就是这样做到的
示例2:
@IntDef(flag=true, value={
DISPLAY_USE_LOGO,
DISPLAY_SHOW_HOME,
DISPLAY_HOME_AS_UP,
DISPLAY_SHOW_TITLE,
DISPLAY_SHOW_CUSTOM
})
@Retention(RetentionPolicy.SOURCE)
public @interface DisplayOptions {
// ...
}
@IntDef注解中,还可以指定flag的值(若不指定则为false),如果我们将flag设为true,则表示可以将允许常量与标志(例如,|、& 和 ^,等等)相结合
代码可访问性注解
@Keep注解用于标注类或方法在混淆的时候将不会被混淆
其他常见的注解
@TargetApi注解用于屏蔽IDE提示要求某一新API版本的错误
示例,一段代码要求Android5.0才行效,但我们又明确知道非Android5.0的情况不会调用到该段代码:
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public void test() {
// ...
}
@SuppressLint注解跟@TargetApi注解类似,不同的是@SuppressLint注解用于屏蔽IDE提示要求的一切API版本的错误,注意是一切
@Widget注解用于表示该类是自定义的Widget类