定義
標(biāo)記接口(marker interface):沒有包含方法聲明的接口屿衅,而只是指明一個類實(shí)現(xiàn)了具有某種屬性的接口杯活。例如裕循,Serializable接口。
標(biāo)記注解(marker annotation):特殊類型的注解颅停,其中不包含成員。標(biāo)記注解的唯一目的就是標(biāo)記聲明掠拳。例如癞揉,@Override。
優(yōu)缺點(diǎn)對比
標(biāo)記接口的優(yōu)點(diǎn):
-
標(biāo)記接口定義的類型是由北標(biāo)記類的實(shí)例實(shí)現(xiàn)的溺欧;標(biāo)記接口則沒有定義的類型喊熟;
這個類型允許你在編譯時捕捉在使用標(biāo)記注解的情況下要到運(yùn)行時才能捕捉到的錯誤 .
比如:就Serializable標(biāo)記接口而言,如果他的參數(shù)沒有實(shí)現(xiàn)該接口姐刁,那么ObjectOutputStream.writeObject(Object, obj)方法將會失敗芥牌。令人不解的是ObjectOutputStream API的創(chuàng)建者在聲明write方法的時候并沒有利用Serializable接口。因此如果將沒有實(shí)現(xiàn)Serializable的對象上調(diào)用ObjectOutputStream.write龙填,只會在運(yùn)行時失敗胳泉。
pic2.jpg
pic1.jpg
-
標(biāo)記接口可以更加精確的進(jìn)行鎖定。
如果注解類型利用@Target(ElementType.TYPE)聲明岩遗,它就可以被應(yīng)用到任何類或者接口扇商,假設(shè)有一個標(biāo)記只是適用于特殊的接口實(shí)現(xiàn),但它卻可以被應(yīng)用到類宿礁,如果定義成一個標(biāo)記接口案铺,就可以用它將唯一的接口擴(kuò)展成適用的接口。
Set接口就可以說是這種有限制的標(biāo)記接口梆靖。他只是用與Collection子類型控汉,但是他不會添加除了Collection定義之外的方法。一般情況下返吻,不把它當(dāng)作標(biāo)記接口姑子,因?yàn)樗倪M(jìn)了幾個Collection的方法的契約,比如add测僵、equals和hashCode街佑。但是很容易想象只適用于某種特殊接口的子類型的標(biāo)記接口,他沒有改進(jìn)接口的任何方法的契約捍靠。這種標(biāo)記接口可以描述整個對象的某個約束條件沐旨,或者表明實(shí)例能夠利用其他某個類的方法進(jìn)行處理(就像Serializable接口表明實(shí)例可以通過ObjectOutputStream進(jìn)行處理一樣)。
聲明set注解:
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Set {
}
@Set
public class HashSet {
}
set接口:
public interface Set<E> extends Collection<E> {
// Query Operations
/** @param e element to be added to this set
* @return <tt>true</tt> if this set did not already contain the specified
* element
* @throws UnsupportedOperationException if the <tt>add</tt> operation
* is not supported by this set
* @throws ClassCastException if the class of the specified element
* prevents it from being added to this set
* @throws NullPointerException if the specified element is null and this
* set does not permit null elements
* @throws IllegalArgumentException if some property of the specified element
* prevents it from being added to this set
*/
boolean add(E e);
}
public interface Collection<E> extends Iterable<E> {
/** @param e element whose presence in this collection is to be ensured
* @return <tt>true</tt> if this collection changed as a result of the
* call
* @throws UnsupportedOperationException if the <tt>add</tt> operation
* is not supported by this collection
* @throws ClassCastException if the class of the specified element
* prevents it from being added to this collection
* @throws NullPointerException if the specified element is null and this
* collection does not permit null elements
* @throws IllegalArgumentException if some property of the element
* prevents it from being added to this collection
* @throws IllegalStateException if the element cannot be added at this
* time due to insertion restrictions
*/
boolean add(E e);
}
標(biāo)記注解的優(yōu)點(diǎn)
- 標(biāo)記注解可以通過默認(rèn)的方式添加一個或者多個注解類型元素 , 給已被實(shí)用的注解類型添加更多地信息 榨婆。隨著時間的推移磁携,簡單的編輯注解類型可以演變成更加豐富的注解類型。這種演變對于編輯接口而言則是不可能的良风,因?yàn)樗ǔ2豢赡茉趯?shí)現(xiàn)接口之后再給他添加方法谊迄。
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
/**
* Created by Jiang Meiwei on 2017/6/27.
*/
@Retention(RUNTIME)
@Target(ElementType.METHOD)
public @interface ExceptinTest{
Class<? extends Exception> value();
}
package com.thunisoft.yilian.lafx.server.ajsc;
/**
* Created by Jiang Meiwei on 2017/6/28.
*/
public class Sample {
@ExceptinTest(ArithmeticException.class)
public static void m1(){
int i = 0;
i = i/i;
}
@ExceptinTest(ArithmeticException.class)
public static void m2(){
int[] a = new int[0];
int i = a[1];
}
@ExceptinTest(ArithmeticException.class)
public static void m3(){
}
}
package com.thunisoft.yilian.lafx.server.ajsc;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
/**
* Created by Jiang Meiwei on 2017/6/28.
*/
public class RunTests {
public static void main(String[] args) throws ClassNotFoundException{
int tests = 0;
int passed = 0;
Class testClass = Class.forName("com.thunisoft.yilian.lafx.server.ajsc.Sample");
for (Method m: testClass.getDeclaredMethods()) {
if(m.isAnnotationPresent(ExceptinTest.class)){
tests++;
try {
m.invoke(null);
}catch (InvocationTargetException wrappedEx) {
Throwable exc = wrappedEx.getCause();
Class<? extends Exception> excType = m.getAnnotation(ExceptinTest.class).value();
if(excType.isInstance(exc)){
passed++;
} else {
System.out.printf("Test %s failed: exppected %s, got %s%n", m, excType.getName(), exc);
}
} catch(Exception exc){
System.out.printf("Test %s failed: on exception%n", m);
}
}
}
}
}
輸出結(jié)果:
pic3.jpg
做了如下更改:
pic4.jpg
- 標(biāo)記注解是更大注解機(jī)制的一部分 , 這意味這它在那些支持注解作為編程元素之一的框架中同樣具有一致性
標(biāo)記注解和標(biāo)記接口的選擇
- 如果標(biāo)記是應(yīng)用到程序元素而不是類或者接口闷供,就必須用注解;
- 如果標(biāo)記只應(yīng)用給類和接口鳞上,那么就標(biāo)記接口優(yōu)先于標(biāo)記注解这吻;
- 如果要定義一個任何新方法都不會與之關(guān)聯(lián)的類型,標(biāo)記接口是最好的選擇篙议;
- 如果想要標(biāo)記程序元素而非類和接口唾糯,考慮到未來可能要給標(biāo)記添加更多的信息忙活著標(biāo)記更適合于已經(jīng)廣泛使用了注解類型的框架,那么就該使用標(biāo)記注解鬼贱。
總結(jié):從某種意義上說移怯,本條目與第19條中“如果不想定義類型就不要使用接口”的說法相反。本條目接近的意思是說:如果想要定義類型这难,一定要使用接口舟误。