标记接口(marker interface)是没有包含方法声明的接口,而只是指明(或者“标明”)一个类实现了具有某种属性的接口。例如,考虑Serializable接口。通过实现这个接口,类表明他的实例可以被写到ObjectOutputStream(或者“被序列化”)。
你可能听说过标记注解使得标记接口过时了。这种断言是不正确的。标记接口有两点胜过标记注解。首先,也是最重要的一点是,标记接口定义的类型是由被标记类的实例实现的;标记注解则没有定义这样的类型。这个类型允许你在编译时捕捉在使用标记注解的情况下要到运行时才能捕捉到的错误。
标记接口胜过标记注解的另一个优点是,他们可以被更加精确的进行锁定。如果注解类型利用@Target(ElementType.TYPE)声明,他就可以被应用到任何类或者接口。假设有一个标记只适用于特殊接口的实现。如果将他定义成一个标记接口,就可以用它将唯一的接口扩展成他适用的接口。
标记注解胜过标记接口的最大优点在于,他可以通过默认的方法添加一个或者多个注解类型元素,给已有使用的注解类型添加更多的信息。随着时间的推移,简单地标记注解类型可以演变成更丰富的注解类型。这种演变对于标记接口而言则是不可能的,因为他通常不能在实现接口之后再给他添加方法。
标记注解的另一个优点在于,他们是更大的注解机制的一部分。因此,标记注解在那些支持注解作为编程元素之一的框架中同样具有以执行。
那么什么时候应该使用标记注解,什么时候应该使用标记接口呢?很显然,如果标记是应用到任何程序元素而不是类或者接口,就必须使用注解,因为只有类和接口可以用来实现或者扩展接口。如果标记只应用给类和接口,就要问问自己:我要编写一个还是多个只接受有这种标记的方法呢?如果是这种情况,就应该优先使用标记接口而非注解。这样你就可以用接口作为相关方法的参数类型,他真正可以为你提供编译时进行类型检查的好处。
如果你对第一个问题的回答是否定的,就要再问问自己:我要永远限制这个标记只用于特殊接口的元素呢?如果是最好将标记定义成该接口的一个子接口。如果这两个问题的答案都是否定的,或许就应该使用标记注解。
总而言之,标记接口和标记注解都各有用处。如果想要定义一个任何新方法都不会与之关联的类型,标记接口就是最好的选择。如果想要标记程序元素而非类和接口,考虑到未来可能要给标记添加更多的信息,或者标记要适合于已经广泛使用了注解类型的框架,那么标记注解就是正确的选择。如果你发现自己在编写的是目标为ElementType.TYPE的标记注解类型,就要花点时间考虑清楚,他是否真的应该为注解类型,想想标记接口是否会更加适合呢。
从某种意义上说,最接近的意义是说:如果想要定义类型,一定要使用接口。