※自定annotation
public @interface Chess {//自動繼承java.lang.annotation的Annotation public String key(); }
用法:
/*如果不是叫key,叫value,而且只有宣告一個屬性的時候,有個特殊能力; 就是@Chess(value="xxx"), value=可以不打,變成@Chess("xxx"),
又或者有其他屬性,且全部都有default時,也可以這樣用,所以叫value是有好處的
*/ @Chess(key="宣告的是String,所以打String內容") class xxx{}
兩個變數以上要這樣:
public @interface Chess {//自動繼承java.lang.annotation的Annotation public String key(); public int value() default 10;//注意只能使用基本型態,不然會報「only primitive type, String, Class, annotation, enumeration are permitted or 1-dimensional arrays thereof」的錯 }
@Chess(key="ooo", value=8) //@Chess(key="ooo") 因為有預設值10,所以可以不打,沒預設值一定要有,不然會報錯 class xxx{}
至於定義了annotation要做哪些事,要配合reflection,先介紹完內建的annotation再說
-------------------------------------------------------------------------------------------------------------
以下介紹 java.lang 的 Annotation Types,因為annotation裡面還有annotation,可能要看兩次才有辦法了解
※@Deprecated(已過時)
原始碼:@Documented @Retention(RetentionPolicy.RUNTIME) @Target(value={CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, PARAMETER, TYPE}) public @interface Deprecated { }
只要是有過時的方法、屬性…等(可以參考下面的@Target),就可以加這個annotation。
別人要使用時,就會發現有刪除線和警告,內行的就知道此方法有可能快移除了, 既然要移除了,那就代表有新的方法,記得還要提供別人現在都是用什麼方法
※@FuntionalInterface(1.8功能型介面)
原始碼:@Documented @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) public @interface FunctionalInterface { }
1.8 的新同學,在interface裡以前只能定義抽象,1.8可以用static實作方法,還可以用default的東東寫預設值,其他方法當然和以前一模一樣; 但是,加上這個annotation,只能定義一個方法,少於一個或二個以上都不行,當然不包括default和static
※@Override(覆寫)
原始碼:@Target(ElementType.METHOD) @Retention(RetentionPolicy.SOURCE) public @interface Override { }
要實作一個介面時,有可能打錯字或大小寫不一樣,那就不是Override了,這時可以加這個annotation來保證一定是override
※@SafeVarargs(安全的變數參數)
原始碼:@Documented @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.CONSTRUCTOR, ElementType.METHOD}) public @interface SafeVarargs { }
此為1.7才有的,官方舉的範例(我稍為修改過)如下:
public static void doSome(List<String>... stringLists) { Object[] array = stringLists; List<Integer> tmpList = Arrays.asList(42); array[0] = tmpList; String s = stringLists[0].get(0); }
呼叫時,這樣使用:
List<String> list1 = new ArrayList<>(); List<String> list2 = new ArrayList<>(); doSome(list1, list2);
設計者的第3行,傳Integer後,第4行居然把它給array接收,但是編譯器不會發現,
所以到第5行就出 java.lang.Integer cannot be cast to java.lang.String 的錯了
根據這樣的問題,希望設計者能考慮這個問題,所以要設計者增加這個annotation,表示他真的有想過這個問題且已經修正了。
但這不是強制的,如果不修正,且加上這個annotation,執行時照樣會報錯
@SuppressWarnings(抑制警告)
原始碼:@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE}) @Retention(RetentionPolicy.SOURCE) public @interface SuppressWarnings { String[] value(); }
在1.5有泛型時,如果不給泛型,Eclipse會黃黃的,這不代表你很色,也不代表你拉屎完沒擦屁股,是代表你不指定泛型有可能在runtime時會出錯,所以叫你指定一下,至少在編譯時期就能發現這個錯。
如果你堅持不加泛型,又不想看到黃黃的,就加個@SuppressWarnings(value={"unchecked"})
那麼像unchecked還有什麼呢?我找了好久,找到個連結,這裡
我在網上發現有神人翻譯如下:
all:抑制所有警告
boxing:抑制與封裝/拆裝作業相關的警告
cast:抑制與強制轉型作業相關的警告
dep-ann:抑制與淘汰註釋相關的警告
deprecation:抑制與淘汰的相關警告
fallthrough:抑制與 switch 陳述式中遺漏 break 相關的警告
finally:抑制與未傳回 finally 區塊相關的警告
hiding:抑制與隱藏變數的區域變數相關的警告
incomplete-switch:抑制與 switch 陳述式 (enum case) 中遺漏項目相關的警告
javadoc:抑制與 javadoc 相關的警告
nls:抑制與非 nls 字串文字相關的警告
null:抑制與空值分析相關的警告
rawtypes:抑制與使用 raw 類型相關的警告
resource:抑制與使用 Closeable 類型的資源相關的警告
restriction:抑制與使用不建議或禁止參照相關的警告
serial:抑制與可序列化的類別遺漏 serialVersionUID 欄位相關的警告
static-access:抑制與靜態存取不正確相關的警告
static-method:抑制與可能宣告為 static 的方法相關的警告
super:抑制與置換方法相關但不含 super 呼叫的警告
synthetic-access:抑制與內部類別的存取未最佳化相關的警告
sync-override:抑制因為置換同步方法而遺漏同步化的警告
unchecked:抑制與未檢查的作業相關的警告
unqualified-field-access:抑制與欄位存取不合格相關的警告
unused:抑制與未用的程式碼及停用的程式碼相關的警告
在寫Hibernate時,常常會用到這一段
Query query = session.createQuery("HQL語法"); List<Class名稱> list = query.list();//這行要我unchecked抑制警告
我想說是不是認為query.list()有可能會null,加個if判斷; 還有網路上說可以用
List<Class名稱> list2 = Collections.checkedList(query.list(), Class名稱.class);
結果還是要unchecked抑制警告,反而白忙一場。
有人是這樣解決的,因為Hibernate有一個HibernateUtil,所以他在裡面加個static泛型方法,如下:
public static <T> List<T> list(Query q) { @SuppressWarnings("unchecked")//在這裡抑制警告 List<T> list = q.list(); return list; }
使用時,只要像這下面這樣就可以不用抑制警告了
List<class名稱> list = HibernateUtil.list(query);
還可以這樣
List<?> list = query.list(); for(Object o:list){ ClassName cl = (ClassName) o; System.out.println(cl.getEmpno()); System.out.println(cl.getJob()); }
-------------------------------------------------------------------------------------------------------------
以下介紹 java.lang.annotation 的 Annotation Types
※@Documented(做java文件)
原始碼:
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Documented {
}
定義一個Chess的annotation,把它定位在 只能放在方法和TYPE上
@Documented
@Target(value={ElementType.METHOD, ElementType.TYPE})
public @interface Chess {
public String key();
public int value() default 32;
}
使用@Chess,和寫一些有的沒的
@Chess(key = "棋", value = 999)
public class Demo {
/**
* 哈囉!註解
*
* @return
*/
@Chess(key = "棋的資訊", value = 881)
public String getInfo() {
return "Hello Annotation";
}
}
在dos裡打上藍色的部分
D:\>cd workspace\Test\src
D:\workspace\Test\src>javadoc -d xxx Demo2.java
Loading source files for package Demo2.java...
javadoc: warning - No source files for package Demo2.java
Constructing Javadoc information...
javadoc: warning - No source files for package Demo2.java
javadoc: error - No public or protected classes found to document.
1 error
2 warnings
D:\workspace\Test\src>javadoc -d xxx Demo.java
Loading source file Demo.java...
Constructing Javadoc information...
Standard Doclet version 1.7.0_60
Building tree for all the packages and classes...
Generating doc\Demo.html...
Demo.java:9: warning - @return tag has no arguments.
Generating doc\package-frame.html...
Generating doc\package-summary.html...
Generating doc\package-tree.html...
Generating doc\constant-values.html...
Building index for all the packages and classes...
Generating doc\overview-tree.html...
Generating doc\index-all.html...
Generating doc\deprecated-list.html...
Building index for all classes...
Generating doc\allclasses-frame.html...
Generating doc\allclasses-noframe.html...
Generating doc\index.html...
Generating doc\help-doc.html...
1 warning
-d xxx 是目錄名稱,也可以不指定,但我生成時檔案加資料夾有16個,會和其他檔案搞混,所以做個資料夾,注意紅色的錯誤,需要有public或protected的class,因為我本來寫在同一個.java裡,但一個.java,只能有一個public,使用protected,也會報「Illegal modifier for the class AA; only public, abstract & final are permitted」的錯,所以只好寫一支新的.java,就可以使用public了。
點index.html就可以看到文件了,中文有些是用\uxxxx的,看起來像是國際化的東東,但是有些又能正常顯示,很詭異
※@Inherited(annotation是否可被繼承)
原始碼:@Documented @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.ANNOTATION_TYPE) public @interface Inherited { }
先定義一個annotation,記得要用runtime才可以,不然保存在.java和.class沒什麼用
@Inherited @Retention(RetentionPolicy.RUNTIME) public @interface Chess { public String key(); public String value(); public int ooo(); }
然後定義父類和子類,子類沒有用annotation
@Chess(key = "keykey", ooo = 10, value = "valval") class Papa {} class Son extends Papa{}
測試看看能不能得到父類的annotation
Class cls = Class.forName("Son"); for(Annotation anno:cls.getAnnotations()){ System.out.println(anno); } if(cls.isAnnotationPresent(Chess.class)){ Chess chess =cls.getAnnotation(Chess.class); //以下三行為.annotation的方法名稱 System.out.println(chess.key()); System.out.println(chess.value()); System.out.println(chess.ooo()); }
結果:
@Chess(key=keykey, ooo=10, value=valval)
keykey
valval
10
※@Native
原始碼:@Documented @Target(ElementType.FIELD) @Retention(RetentionPolicy.SOURCE) public @interface Native { }
1.8的新同學,這個我也看沒有,不過反正一定是和C++有關的東西!
※@Repeatable(重覆宣告annotation)
原始碼:@Documented @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.ANNOTATION_TYPE) public @interface Repeatable { Class<? extends Annotation> value(); }
這也是1.8的新同學,讓annotation可以重覆宣告,例如以前一定要這樣寫
@SuppressWarnings({"unchecked", "unused"})
加上這個annotation就可以這樣用
@Repeatable
@SuppressWarnings("unchecked")
@SuppressWarnings("unused")
也就是有不同的撰寫風格啦
※@Retention(保存範圍)
原始碼:
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Retention {
RetentionPolicy value();
}
@Target有一個@Retention,裡面要放RetentionPolicy,總共分成三種範圍,如下:
.java .class JVM
SOURCE ✔
CLASS ✔ ✔
RUNTIME ✔ ✔ ✔
CLASS為預設,像@Target是RetentionPolicy.RUNTIME,所以會保存在.java、.class、在執行時也會加載到JVM中
※@Target(限制annotation放在哪)
以上的例子,放在方法、類別…等都可以,如果要限制只能放在enum上呢?原始碼:
@Documented @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.ANNOTATION_TYPE) public @interface Target { ElementType[] value(); }
value裡面要放 ElementType,API 有詳細的介紹,且英文單字不難,但它的連結好像沒做書籤,所以我把它複製並翻譯過來:
ANNOTATION_TYPE 宣告在annotation
CONSTRUCTOR 宣告在建構子
FIELD 宣告在屬性(包括Enum)
LOCAL_VARIABLE 宣告在區域變數
METHOD 宣告在方法
PACKAGE 宣告在package
PARAMETER 宣告在參數
TYPE 宣告在類別、介面(包括annotation)、enum
以下兩個為1.8新增的
TYPE_PARAMETER 宣告在TYPE的角括號(<>),(Type parameter declaration)
TYPE_USE 宣告在各式型態(Use of a type)
所以ElementType.ANNOTATION_TYPE只能宣告在annotation裡; enum可以用ElementType.Field 或 ElementType.Type
我親自試過後,
ANNOTATION_TYPE:宣告在annotation,包括目前自己定義的annotation和annotation裡的方法,但其他的annotation裡的方法不行,如下:
@ABC @Target(ElementType.ANNOTATION_TYPE) @interface ABC { @ABC//如果這@interface不是ABC就不行 public String value() default ""; }
CONSTRUCTOR:除了建構子,還包括自己的annotation和annotation裡的方法,如上面的例子,都可以
FIELD:除了全域變數,還包括自己的annotation和annotation裡的方法,API說包括enum,其實指得是enum裡的field
LOCAL_VARIABLE:除了區域變數,還包括自己的annotation和annotation裡的方法
METHOD:除了方法,還包括自己的annotation和annotation裡的方法,還有其他annotation的方法都OK
PACKAGE:直接定義會出現「Package annotations must be in file package-info.java」的錯,所以我只好新增一個叫package-info.java的檔,打開以後是空的,有錯誤提示「The declared package "" does not match the expected package "xxx"」,所以我就宣告個package xxx,然後在前面用annotation就可以了,原來的檔不能定義。
當然還包括自己的annotation和annotation裡的方法
PARAMETER:宣告在()裡,還包括自己的annotation和annotation裡的方法
TYPE:宣告在class、interface、annotation、enum,abstract算class的一種,當然還包括自己的annotation和annotation裡的方法
TYPE_PARAMETER:宣告在泛型類別和泛型方法,還有自己的annotation。注意自己的annotation方法不行,還有List、Set、Map裡的<>也不行
TYPE_USE:只有一個不行java.lang.String不行(不是String不行,只要是寫完整路徑就不行,很奇怪; 我只寫String是OK的,滑鼠移上去,也真的是java.lang.String,但就是一個可以,一個不行),是完整路徑不行喔!譬如java.util.Map不行,但import在上面,只寫Map卻可以
@Target都不寫:TYPE_PARAMETER不行以外,其他都可以
全部都可以:要這樣設@Target({ ElementType.TYPE_USE, ElementType.FIELD, ElementType.LOCAL_VARIABLE }),但兩個以上不知為什麼,Eclipse沒提示
P.S. 自己定義的@interface一直都可以,只有TYPE_PARAMETER,自己的方法不行
List、Set、Map,<>裡定義annotation,只有TYPE_USE可以,連不寫@Target都不行
所以依照這樣的關係,我整理了一張表(我測的是1.8.0_25):
※使用annotation並做事
先寫一支annotation,我加個繼承
@Inherited @Retention(RetentionPolicy.RUNTIME) public @interface Chess { public String key() default "我是key"; public String value(); public int ooo() default 32; }
然後有一個爸爸類,類和方法都有加annotation,爸爸有兩個兒子繼承,第一個兒子覆寫爸爸的方法,第二個兒子什麼都沒有,而且兒子都沒有用@Chess
@Chess(key = "keykey", ooo = 10, value = "valval") class Papa { @Chess("我是value") public String getInfo() { return "Papa的getInfo()"; } } class FirstSon extends Papa { @Override public String getInfo() { return "FirstSon的getInfo()"; } } class SecondSon extends Papa { }
寫個測試類來測一下:
System.out.println("----------------Papa-------------------"); Class<?> papaClass = Class.forName("Papa"); System.out.println("從class取得annotation,然後取得一個一個屬性"); Chess papaChess = papaClass.getAnnotation(Chess.class); System.out.println(papaChess.ooo()); System.out.println(papaChess.key()); System.out.println(papaChess.value()); System.out.println("從class取得annotations,然後取得全部屬性"); for (Annotation ann : papaClass.getAnnotations()) { System.out.println(ann); } System.out.println("從方法取得annotation,然後取得一個一個屬性"); Chess papaChessMethod = papaClass.getMethod("getInfo").getAnnotation( Chess.class); System.out.println(papaChessMethod.ooo()); System.out.println(papaChessMethod.key()); System.out.println(papaChessMethod.value()); System.out.println("從方法取得annotation,然後取得全部屬性"); for (Annotation ann : papaClass.getMethod("getInfo").getAnnotations()) { System.out.println(ann); } System.out.println("----------------SecondSon--------------"); Class<?> secondClass = Class.forName("SecondSon"); Chess secondChess = secondClass.getAnnotation(Chess.class); System.out.println("從class取得annotation,然後取得一個一個屬性"); if (secondChess != null) { System.out.println(secondChess.ooo()); System.out.println(secondChess.key()); System.out.println(secondChess.value()); } System.out.println("從class取得annotations,然後取得全部屬性"); for (Annotation ann : secondClass.getAnnotations()) { System.out.println(ann); } System.out.println("從方法取得annotation,然後取得一個一個屬性"); Chess secondChessMethod = secondClass.getMethod("getInfo") .getAnnotation(Chess.class); if (secondChessMethod != null) { System.out.println(secondChessMethod.ooo()); System.out.println(secondChessMethod.key()); System.out.println(secondChessMethod.value()); } System.out.println("從方法取得annotation,然後取得全部屬性"); for (Annotation ann : secondClass.getMethod("getInfo").getAnnotations()) { System.out.println(ann); } System.out.println("----------------FirstSon---------------"); Class<?> firstClass = Class.forName("FirstSon"); Chess firstChess = firstClass.getAnnotation(Chess.class); System.out.println("從class取得annotation,然後取得一個一個屬性"); if (firstChess != null) { System.out.println(firstChess.ooo()); System.out.println(firstChess.key()); System.out.println(firstChess.value()); } System.out.println("從class取得annotations,然後取得全部屬性"); for (Annotation ann : firstClass.getAnnotations()) { System.out.println(ann); } System.out.println("從方法取得annotation,然後取得一個一個屬性"); Chess firstChessMethod = firstClass.getMethod("getInfo").getAnnotation( Chess.class); if (firstChessMethod != null) { System.out.println(firstChessMethod.ooo()); System.out.println(firstChessMethod.key()); System.out.println(firstChessMethod.value()); } System.out.println("從方法取得annotation,然後取得全部屬性"); for (Annotation ann : firstClass.getMethod("getInfo").getAnnotations()) { System.out.println(ann); }
結果:
----------------Papa-------------------
從class取得annotation,然後取得一個一個屬性
10
keykey
valval
從class取得annotations,然後取得全部屬性
@Chess(ooo=10, key=keykey, value=valval)
從方法取得annotation,然後取得一個一個屬性
32
我是key
我是value
從方法取得annotation,然後取得全部屬性
@Chess(ooo=32, key=我是key, value=我是value)
----------------SecondSon--------------
從class取得annotation,然後取得一個一個屬性
10
keykey
valval
從class取得annotations,然後取得全部屬性
@Chess(ooo=10, key=keykey, value=valval)
從方法取得annotation,然後取得一個一個屬性
32
我是key
我是value
從方法取得annotation,然後取得全部屬性
@Chess(ooo=32, key=我是key, value=我是value)
----------------FirstSon---------------
從class取得annotation,然後取得一個一個屬性
10
keykey
valval
從class取得annotations,然後取得全部屬性
@Chess(ooo=10, key=keykey, value=valval)
從方法取得annotation,然後取得一個一個屬性
從方法取得annotation,然後取得全部屬性
P.S. 注意第一個兒子他覆寫爸爸的方法,所以通過方法取不到annotation; 而第二個兒子都取得到
如果把@Inherited拿掉的結果如下:
----------------Papa-------------------
從class取得annotation,然後取得一個一個屬性
10
keykey
valval
從class取得annotations,然後取得全部屬性
@Chess(ooo=10, key=keykey, value=valval)
從方法取得annotation,然後取得一個一個屬性
32
我是key
我是value
從方法取得annotation,然後取得全部屬性
@Chess(ooo=32, key=我是key, value=我是value)
----------------SecondSon--------------
從class取得annotation,然後取得一個一個屬性
從class取得annotations,然後取得全部屬性
從方法取得annotation,然後取得一個一個屬性
32
我是key
我是value
從方法取得annotation,然後取得全部屬性
@Chess(ooo=32, key=我是key, value=我是value)
----------------FirstSon---------------
從class取得annotation,然後取得一個一個屬性
從class取得annotations,然後取得全部屬性
從方法取得annotation,然後取得一個一個屬性
從方法取得annotation,然後取得全部屬性
----------------Papa-------------------
從class取得annotation,然後取得一個一個屬性
10
keykey
valval
從class取得annotations,然後取得全部屬性
@Chess(ooo=10, key=keykey, value=valval)
從方法取得annotation,然後取得一個一個屬性
32
我是key
我是value
從方法取得annotation,然後取得全部屬性
@Chess(ooo=32, key=我是key, value=我是value)
----------------SecondSon--------------
從class取得annotation,然後取得一個一個屬性
從class取得annotations,然後取得全部屬性
從方法取得annotation,然後取得一個一個屬性
32
我是key
我是value
從方法取得annotation,然後取得全部屬性
@Chess(ooo=32, key=我是key, value=我是value)
----------------FirstSon---------------
從class取得annotation,然後取得一個一個屬性
從class取得annotations,然後取得全部屬性
從方法取得annotation,然後取得一個一個屬性
從方法取得annotation,然後取得全部屬性
P.S. 第一個兒子什麼都取不到了,第二個兒子通過方法,還是可以取得老爸的annotation
所以我試的結果,@Inherited只有對類別有用的樣子
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.MODULE)
public @interface Uuu {
String value();
}
※新增一個 Uuu 使用在 module 上
@Uuu("")
module Xxx {
...
}
可以在 module-info 使用
※java 9 新增 MODULE
@Documented@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.MODULE)
public @interface Uuu {
String value();
}
※新增一個 Uuu 使用在 module 上
@Uuu("")
module Xxx {
...
}
可以在 module-info 使用
沒有留言:
張貼留言