­

Java 中的 tagging interface

  • 2020 年 3 月 27 日
  • 筆記

什么是 tagging interface?

在 Servlet 源码中,所有的监听器类都实现了空接口 EventListener,代码如下所示:

package java.util;    /**   * A tagging interface that all event listener interfaces must extend.   * @since JDK1.1   */  public interface EventListener {  }

说到这是一个 tagging interface,也就是标签接口,所有的时间监听器接口都需要继承这个接口。其次,显而易见的是,这个接口没有任何方法或者抽象方法。

那么 tagging interface 是什么意思呢?

在 Java 中实现或继承接口通常是为了给当前类或接口某种功能。既然 tagging interface 没有抽象方法,那么此接口就不是用于赋予实现此接口的类某种功能。

所以说,如果以 tagging interface 进行面向接口编程并不是一个好选择。具体来说,如下代码所示:

class MyListener implements EventListener{    //声明一些类方法    publiv void foo(){      }  }    class Test{    public void test (EventListener el){      (MyListener)el.foo();    }  }

因为 EventListener 接口是一个空接口,所以即使面向此接口编程,只会落到在此申明下无法调用任何方法的境地,所以必须要强制类型转换。而一旦强制类型转换,那么面向接口编程实际上也失去了意义。

所以虽然 tagging interface 可以告诉程序员:这是一个属于某种类型的类或接口,但是程序员并不能从中收益。标签的意义在于告诉 VM 这是一个事件监听类,你可以为此采用一些特殊的优化技巧(比如 Serializable 也是个标签类,它用于告诉 JVM 可以进行为实现此接口的类序列化以及采用一些优化手段)。

或者可以进行类型检查,符合要求才进行下一步操作,比如:

if(el instanceof EventListner){    el.foo();  }

tagging interface 的历史悠久,实际上在 JDK 1.5 提供的注解开始,其一部分功能被取代了。

比如:

@Serializable()  public class MyClass {    }

注解 @Serializable() 给 MyClass 类打上了相应标签,为了检查当前类是否打上对应标签,可以使用 Class 对象的 isAnnotationPresent 方法,而不是 instanceof 关键字,如下所示:

MyClass mc =new MyClass();  if(mc.getClass().isAnnotationPresent(Serializable.class)){      //调用方法  }

可见,注解和 tagging interface 类可以起到相同的作用:给类/接口打上标签。


另一方面,注解和 tagging interface 有着一定的区别。

tagging interface 比注解有着更强的限制能力。这是因为注解如果能作用于类,那么此注解必然使用了元注解 @ElementType.TYPE,也就表明这个注解能够打在任何其他类上。而 tagging interface 通过继承的技巧能够限定相关类必须实现某个其他接口。

interface Marker extends Foo {    }

如上的 Marker 是一个 tagging interface,但是其额外继承于 Foo 接口,所以实现了 Marker 接口的类虽然不需要实现来自 Marker 接口的任何方法,但是要实现来自 Foo 的方法。这便是带有 Foo 接口限制的标签接口 Marker。