Java提供了一些標準標註,先前經常看到的@Override
就是標準標註,它在原始碼中提供編譯器的訊息是,被標註的方法必須是父類別或介面中已定義的方法,請編譯器協助是否真的為重新定義方法。例如,在重新定義Thread
的run()
方法時:
public class WorkerThread extends Thread {
public void Run() {
//...
}
}
這個程式範例中,本來重新定義run()
方法,結果打成了Run()
方法,這變成在WorkerThread
中定義新方法。為了避免這類錯誤,可以加上@Override
,編譯器看到@Override
這個標註,瞭解必須檢查父類別中是否存在Run()
方法,但父類別實際上並沒有這個方法,所以會回報錯誤:
如果某個方法原先存在於API中,後來不建議再使用,可以於該方法上標註
@Deprecated
。例如:public class Some {
@Deprecated
public void doSome() {
...
}
}
編譯後的.class會儲存這個資訊,若有使用者後續又想呼叫或重新定義這個方法,編譯器會提出警訊(IDE通常會在方法加上刪除線表示):
Note: XXX.java uses or overrides a deprecated API.
Note: Recompile with -Xlint:deprecation for details.
Note: Recompile with -Xlint:deprecation for details.
在JDK5之後加入泛型支援,對於支援泛型的API,建議明確指定泛型真正型態,如果沒有指定的話,編譯器會提出警訊。例如程式碼若含有以下片段:
public void doSome() {
List list = new ArrayList();
list.add("Some");
}
由於
List
與ArrayList
支援泛型,但這邊沒有指定泛型真正型態,編譯時會出現以下訊息:Note: xxx.java uses unchecked or unsafe operations.
Note: Recompile with -Xlint:unchecked for details.
Note: Recompile with -Xlint:unchecked for details.
如果不想看到這個警訊,可以使用
@SuppressWarnings
指定抑制unckecked的警訊產生:@SuppressWarnings(value={"unchecked"})
public void doSome() {
List list = new ArrayList();
list.add("Some");
}
@SuppressWarnings
的value
可以指定要抑制的警訊種類。例如你真的想呼叫@Deprecated
標示過的方法,又不想看到警訊,可以如下:@SuppressWarnings(value={"deprecation"})
也可以一次指定抑制多項警訊:
@SuppressWarnings(value={"unchecked", "deprecation"})
JDK7新增了
@SafeVarargs
標註,這得先談談一個問題,你有沒有可能建立List<String>[]
陣列實例?答案是不行,無論是new List<String>[10]
或List<String>[] lists = {new ArrayList<String>()}
的語法,編譯器都會直接給你一個error: generic array creation的錯誤訊息,然而宣告List<String>[] lists
是可以的,只是實際上不會有人這麼做,可以宣告List<String>[] lists
是為了支援可變長度引數,例如你可能這麼宣告:public class Util {
public static <T> void doSome(List<String>... varargs) {
...略
}
}
程式碼中使用了泛型宣告可變長度引數,在JDK6中這個程式碼可以順利編譯,也不會有任何警訊,而
varargs
實際上就是List<String>[]
型態,但如果你這麼使用:List<String> list1 = new ArrayList<>();
List<String> list2 = new ArrayList<>();
Util.doSome(list1, list2);
編譯器會於呼叫
doSome()
時發出警示訊息:Main.java:7: warning: [unchecked] unchecked generic array creation for varargs parameter of type List<String>[]
Util.doSome(list1, list2);
^
1 warning
Util.doSome(list1, list2);
^
1 warning
Java泛型語法是提供編譯器資訊,使其可在編譯時期檢查型態錯誤,編譯器只能
List<String>
的型態資訊,在編譯時期幫你檢查呼叫doSome()
時,傳入的list1
與list2
是否為List<String>
型態,然而設計doSome()
的人在實作流程時,是有可能發生編譯器無法檢查出來的執行時期型態錯誤。例如SafeVarargs
的API文件中就有個範例: public static <T> void doSome(List<String>... varargs) {
Object[] array = varargs;
List<Integer> tmpList = Arrays.asList(42);
array[0] = tmpList; // 語意不對,不過編譯器不會有警訊
String s = varargs[0].get(0); // 執行時期發生 ClassCastException
}
這類問題稱為heap pollution,也就是編譯器無法檢查出一些執行時期的型態錯誤,問題是即使編譯器提醒身為
doSome()
的客戶端可能會有這類問題發生又如何?問開發doSome()
的人會不會發生問題?自己看doSome()
的原始碼?最好的方式,就是對開發doSome()
的人加以提醒,而不是讓使用doSome()
的人提心吊膽,因此在JDK7中,同樣的Util
類別編譯時,會發生以下警訊:Util.java:3: warning: [unchecked] Possible heap pollution from parameterized vararg type List<String>
public static <T> void doSome(List<String>... varargs) {
^
1 warning
public static <T> void doSome(List<String>... varargs) {
^
1 warning
在使用泛型定義不定長度引數時,編譯器會提示開發人員,有沒有注意到heap pollution問題,如果開發人員確定避免了這個問題,則可以使用
@SafeVarargs
加以標註。例如:public class Util {
@SafeVarargs
public static <T> void doSome(List<String>... varargs) {
...略
}
}
加上
@SafeVarargs
後,編譯Util
類別就不會發生警訊,由於開發人員要在確定避免了heap pollution的情況下,才會加上@SafeVarargs
,呼叫Util.doSome()
的使用者就不用提心吊膽,也就是如下呼叫Util.doSome()
就不會再發生警訊。 List<String> list1 = new ArrayList<>();
List<String> list2 = new ArrayList<>();
Util.doSome(list1, list2);
雖然在JDK7中,也可以如下抑制警訊:
public class Util {
@SuppressWarnings(value={"unchecked"})
public static <T> void doSome(List<String>... varargs) {
...略
}
}
不過這不僅抑制了使用泛型宣告不定長度引數的警訊,連同其它unchecked警訊也會一併抑制,所以並不鼓勵用這個方式解決使用泛型宣告不定長度引數的警訊。
@FunctionalInterface
標註,讓編譯器可協助檢查interface
是否可做為Lambda的目標型態,這在介紹Lambda時還會詳細介紹。