註解的使用有助於減少樣板代碼的編寫,並提供了一種聲明性的方法來描述代碼的意圖和行為。可以用於實現依賴註入,資料庫映射、運行時許可權處理等功能。 ...
註解的使用有助於減少樣板代碼的編寫,並提供了一種聲明性的方法來描述代碼的意圖和行為。可以用於實現依賴註入,資料庫映射、運行時許可權處理等功能。
1. 值約束類型註解
@Nullable // 變數、參數或返回值可能為 null @NonNull // 變數、參數或返回值不能為 null @IntRange // 指定整型或長整型取值範圍, eg: void setAlpha(@IntRange(from=0, to=255) int alpha) {...} @FloatRange // 指定浮點型取值範圍, eg: void setAlpha(@FloatRange(from=0.0, to=1.0) int alpha) {...} @Size // 數組、集合或字元串的長度 @Size(min=2) // 最小長度 @Size(max=2) // 最大長度 @Size(2) // 固定長度 @Size(multiple=2) // 必須為2的整數倍長度
2. 資源類型註解
@AnimatorRes // animator 資源類型 @AnimRes // anim 資源類型 @AnyRes // 任意資源類型 @ArrayRes // array 資源類型 @AttrRes // attr 資源類型 @BoolRes // boolean 資源類型 @ColorRes // color 資源類型 @DimenRes // dimen 資源類型 @DrawableRes // drawable 資源類型 @FractionRes // fraction 資源類型 @IdRes // id 資源類型 @IntegerRes // integer 資源類型 @InterpolatorRes // interpolator 資源類型 @LayoutRes // layout 資源類型 @MenuRes // menu 資源類型 @PluralsRes // plurals 資源類型 @RawRes // raw 資源類型 @StringRes // string 資源類型 @StyleableRes // styleable 資源類型 @StyleRes // style 資源類型 @TransitionRes // transition 資源類型 @XmlRes // xml 資源類型
3. 線程類型註解
@MainThread // 主線程 @UiThread // UI線程,除非不同線程顯示不同UI,否則 @MainThread 與 @UiThread 無差別 @WorkerThread // 工作線程,不能執行UI操作 @BinderThread // 用於執行 ContentProvider 中的 query/insert/update/delete 方法 @AnyThread // 任意線程
4. 許可權類型註解 @RequiresPermission
4.1 使用 @RequiresPermission 註解可以驗證方法調用方是否擁有許可權。
@RequiresPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) public abstract void onSaveToLocal(Bitmap bitmap) throws IOException;
4.3 如果是涉及多個許可權,可以通過 anyOf 和 allOf 來控制判斷條件:
(1) anyOf: 註解許可權列表是否滿足任意一個:
@RequiresPermission(anyOf = { Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE}) public static final void readFile(String dest) { // ... }
(2) allOf: 註解許可權列表是否全部擁有:
@RequiresPermission(allOf = { Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE}) public static final void copyFile(String dest, String source) { // ... }
5. 間接許可權
如果許可權依賴於提供給方法參數的特定值,請對參數本身使用 @RequiresPermission,而不用列出具體許可權。例如, startActivity(Intent) 方法會對傳遞到方法的 intent 使用間接許可權:
public abstract void startCallActivity(@RequiresPermission Intent intent, @Nullable Bundle b) {...}
在您使用間接許可權時,構建工具將執行數據流分析以檢查傳遞到方法的參數是否具有任何 @RequiresPermission 註解。隨後,它們會對方法本身強制參數的任何現有註解。在 startActivity(Intent) 示例中,當一個不具有相應許可權的 intent 傳遞到方法時,Intent 類中的註解會針對 startActivity(Intent) 的無效使用生成警告,例如:
@SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) @RequiresPermission(Manifest.permission.CALL_PHONE) public static final String ACTION_CALL = "android.intent.action.CALL";
public void onCall() { Intent callIntent = new Intent(ACTION_CALL); startCallActivity(callIntent, null); } @Override public void startCallActivity(@RequiresPermission Intent intent, @Nullable Bundle b) { intent.setData(Uri.parse("tel:" + phoneNumber)); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); if (b != null) { intent.setBundle(b); } startActivity(intent); }
6. Java 註解
Java 註解(Annotation)又稱 Java 標註,是 JDK5.0 引入的一種註釋機制。 Java 語言中的類、方法、變數、參數和包等都可以被標註。和 Javadoc 不同,Java 標註可以通過反射獲取標註內容。在編譯器生成類文件時,標註可以被嵌入到位元組碼中。Java 虛擬機可以保留標註內容,在運行時可以獲取到標註內容 。 當然它也支持自定義 Java 標註。
Java內置了三種標準註解,其定義在java.lang中。
@Override // 表示當前的方法定義將覆蓋超類中的方法。 @Deprecated // 被此註解標記的元素表示被廢棄,如果程式員使用了註解為它的元素,那麼編譯器會發出警告。 @SuppressWarnings // 關閉不當的編譯器警告信息, 常用的有: ▪ @SuppressWarnings("unused") // 去除單類型告警 ▪ @SuppressWarnings("unused", "unchecked") // 去除多類型告警 ▪ @SuppressWarnings("all") // 去除全部類型告警
7. 自定義註解
定義新的 Annotation 類型使用 @interface 關鍵字。
public @interface MyAnnotation { String name(); // 字元串 String password() default "abc"; // 帶預設值的字元串 int age() default 18; // 數值類型 Gender gender() default Gender.FEMALE; // 枚舉類型 Class clazz(); // 類類型 MyAnnotation2 my2(); // 註解中包含其他註解 char[] arr() default {'a', 'b', 'c'}; // 數組類型 }
引用:
@MyAnnotation(name="張三", age=38, gender=Gender.MALE, class=String.class,
my2=@MyAnnotion2(name="xxx"), arr={'1','2','3'}) public void set() {...}
8. 元註解
元註解的作用是負責註解其他註解,主要分為6種:
@Retention // 指定其所修飾的註解的保留策略; @Document // 該註解是一個標記註解,用於指示一個註解將被文檔化; @Target // 用來限制註解的使用範圍; @Inherited // 該註解使父類的註解能被其子類繼承; @Repeatable // 該註解是Java8新增的註解,用於開發重覆註解; 類型註解(Type Annotation) // 該註解是Java8新增的註解,可以用在任何用到類型的地方。
8.1 @Retention 註解
@Retention 註解用於指定被修飾的註解可以保留到JVM哪個階段。保留策略值有以下三個:
策略值 | 功能描述 |
Retention.SOURCE | 註解只在保留在源文件中,在編譯期間刪除 |
Retention.CLASS | 註解只在編譯器間存在於.class文件中,運行時JVM不可獲取信息,該策略值也是預設值 |
Retention.RUNTION | 可保留到JVM運行時期間,可以獲取註解信息(反射),也是最長註解持續時間 |
【示例】保留到JVM運行時註解
@Retention(@RetentionPolocy.RUNTIME) // 定義註解
8.2 @Document 註解
@Document 註解用於指定被修飾的註解可以被 javadoc 工具提取成文檔,定義註解類時使用。
@Document 註解進行修飾,則所有使用該註解修飾的程式元素的 API 文檔中將會包含該註解說明。
@Document // 定義註解
8.3 Target 註解
@Target 註解用來限制註解的使用範圍。
@Target({類型1, 類型2, ...})
枚舉值 | 功能描述 |
ElementType.Type | 可以修飾類、介面、註解或枚舉類型 |
ElementType.FIELD | 可以修飾屬性,包括枚舉常量 |
ElementType.METHOD | 可以修飾方法 |
ElementType.PAPAMETER | 可以修飾參數 |
ElementType.CONSTRUCTOR | 可以修飾構造方法 |
ElementType.LOCAL_VARIABLE | 可以修飾局部變數 |
ElementType.ANNOTATION_TYPE | 可以修飾註解類 |
ElementType.PACKAGE | 可以修飾包 |
@Target(ElementType.FIELD) // 定義註解
8.4 @Inherited 註解
@Inherited 註解指定註解具有繼承性,如果摸個註解使用 @Inherited 進行修飾,則該類使用該註解是,其子類將自動被修飾。
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Inherited public @interface InheritedAnno1 { String comment(); int order() default 0; }
// 使用自定義的 @MyInheritedAnno 註解修飾 Base @MyInheritedAnno(comment="繼承註解", order=2) public class Base { } // InheritedDemo 只繼承了 Base 類 // 並被直接使用 @MyInheritedAnno 註解 public class InheritedDemo extends Base {\ public InheritedDemo() { // 從 InheritedDemo 中獲取 MyInheritedAnno 註解信息 MyInheritedAnno anno = InheritedDemo.class.getAnnotation(MyInheritedAnno.class); // 輸出 MyInheritedAnno 註解成員信息 Systen.out.println(anno.comment() + ":" + anno.order()); // 列印 InheritedDemo 類是否具有 @MyInheritedAnno 修飾 System.out.println(InheritedDemo.class.isAnnotationPresent(MyInheritedAnno.class)); } } // 運行結果 繼承註解:2 true
8.5 @Repeatable註解
@Repeatable 註解是 Java8 新增的註解,用於開發重覆註解。在 Java8 之前,同一個程式元素前只能使用一個相同類型的註解,如果需要在同一個元素前使用多個相同類型的註解必須通過註解容器來實現。從 Java8 開始,允許使用多個相同的類型註解來修飾同一個元素,前提是該類型的註解是可重覆的,即在定義註解時要用 @Repeatable 元註解進行修飾。
import java.lang.annotation.*; @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) @interface AnnolContents{//該註解是容器 //定義value成員變數,該成員變數可以接受多個@RepeatableAnnol註解 RepeatableAnnol[] value(); } @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) @Repeatable(AnnolContents.class) public @interface RepeatableAnnol { String name() default "南瓜仔仔"; int age(); }
@RepeatableAnnol(age = 10) @RepeatableAnnol(name = "尋找南瓜", age = 22) public class RepeatableAnnolDemo { public static void main(String[] args) { //使用java8新增的getDeclaredAnnotationsByType()方法獲取 //修飾類的多個@RepeatableAnnol註解 RepeatableAnnol[] annols = RepeatableAnnolDemo.class.getDeclaredAnnotationsByType(RepeatableAnnol.class); //遍歷@RepeatableAnnol註解並顯示 for (RepeatableAnnol annol :annols){ System.out.println(annol.name() + "--->" + annol.age()); } //使用傳統的getDeclaredAnnotation()方法獲取修飾類的@AnnolContents註解 AnnolContents container = RepeatableAnnolDemo.class.getDeclaredAnnotation(AnnolContents.class); System.out.println(container); } }
【運行結果:】
南瓜仔仔--->10
尋找南瓜--->22
@com.ngd.test03.AnnolContents({@com.ngd.test03.RepeatableAnnol(name="\u5357\u74dc\u4ed4\u4ed4", age=10), @com.ngd.test03.RepeatableAnnol(name="\u5bfb\u627e\u5357\u74dc", age=22)})
8.6 類型註解
Java8為ElementType枚舉增加了TYPE_PARAMETER和TYPE_USE兩個枚舉類值,允許在定義枚舉類時使用@Target(ElementType.TYPE_USE)來修飾,此種註解被稱為“類型註解”(Type Annotation)。
在Java8之前,只能在定義類、介面、方法和成員變數等程式元素時使用註解,從Java8開始新增的類型註解可以用在任何用到類型的地方。
除了在定義類、介面和方法等常見的程式元素時可以使用類型註解,還可以在以下幾個位置使用類型註解進行修飾:
- 創建對象(使用new關鍵字創建)
- 類型轉換
- 使用implements實現介面
- 使用throws聲明拋出異常序列
- 方法參數
//定義一個簡單的類型註解,不帶任何成員變數 @Target(ElementType.TYPE_USE) @interface NotNull{ } //定義類時使用@NotNull類型註解 @NotNull public class TypeAnnotationDemo implements @NotNull Serializable {//implements時使用類型註解 //方法形參中使用類型註解 public static void main(@NotNull String[] args) throws @NotNull FileNotFoundException {//throws使用時用類型註解 Object obj = "南瓜仔仔"; //強制類型轉換時使用類型註解 String str = (@NotNull String)obj; //創建對象時使用類型註解 Object win = new @NotNull JFrame("QST_Login"); } //泛型中使用類型註解 public void foo(List<@NotNull String> info){} }
上述代碼在各種情況下使用@ NotNull 類型註解,這種無處不在的類型註解可以讓編譯器執行更嚴格的代碼檢查,從而提高程式的健壯性。需要說明的是,上面的程式雖然大量使用了@ NotNull 類型註解,但這些註解是不會起到任何作用的,因為 Java 8本身並沒有為這些類型註解提供處理工具,不能對類型註解執行檢查框架。因此,如果需要類型註解發揮作用,需要程式員自己實現類型註解檢查框架。目前有些第三方組織發佈了類型註解檢查工具,程式員可以直接使用這些第三方框架提供的檢查工具,從而讓編譯器執行更嚴格的檢查,以保證代碼的健壯性。