可串行标记接口是否包含默认方法?

时间:2014-11-03 21:57:33

标签: java interface java-8 marker-interfaces default-method

我认为它不能,因为标记接口原则是没有任何方法,但由于默认方法不是 abstract 我不确定。

4 个答案:

答案 0 :(得分:12)

A" Marker"就Java而言,接口只是一个常规接口。因此,它可以像任何(Java-8)接口一样具有默认方法。

现在,至于这是否违反了Marker界面的原则,我不得不说是。 Marker接口应该充当各种标志,仅标识一个类符合某些外部标准。现在,它可以是标记界面具有抽象/默认方法,但它将不再纯粹符合定义。

来自 Effective Java(第二版)

  

标记接口是一个不包含方法声明的接口,但只是指定(或“标记”)实现接口具有某些属性的类。

答案 1 :(得分:8)

标记界面是一种设计模式,因此我们可以通过观察定义是什么来开始回答您的问题:

  

在早期版本的Java中,标记接口是声明类的元数据的唯一方法。例如,Serializable Marker Interface允许类的作者在序列化和反序列化时说他们的类将正常运行。

在Java的上下文中,Marker接口的目的是谈论该类。有趣的是,它标记它作为一些东西。这方面的一个示例是Serializable界面,它除了标记Class能够序列化为String之外什么也没做。这里的问题是:

定义是否包含功能?

不,我不这么认为。功能不仅仅是元数据关于类;它有助于定义类本身。从元数据到数据需要一步。因此,就设计模式而言,标记接口不能定义声明功能;它可以简单地说明实施Class

答案 2 :(得分:6)

尽管@Azar的答案是正确的,但我们不要忘记在引入默认方法之前,有效Java是编写的。

什么是标记界面?

有两种查看标记接口的方法:

  1. 它们是声明没有方法的接口。
  2. 它们是不强制执行任何方法的接口。
  3. “官方”定义是第一个,但直到Java 7这两个语句是等效的。它是Effective Java中的一种重复模式,一旦发布了接口,就无法向其添加任何方法,因为它会强制实现新方法。

    然而,这正是默认方法试图解决的问题:允许接口的演进,而无需改进实现它们的所有类。它还使上述两个语句略有不同:默认方法明显违反语句1,并且设计不违反语句2.

    这在实践中意味着什么?

    想象一下,您编写了一个XML序列化引擎,并创建了一个标记接口XmlSerializable来配合它:

    public interface XmlSerializable {}
    

    到目前为止,这么好。但是后来你意识到你实际上有一些需要特殊处理的课程,他们需要提供自己的定制转换器。所以你可能会这样做:

    public interface XmlSerializable {
    
       public static final Map<Class,Class> CONVERTERS = ...
    
       default Class customConverter() {
          return CONVERTERS.get(this.getClass());
       }
    }
    

    这会阻止XmlSerializable成为标记界面吗?你可以说它仍然是一个标记界面,因为你没有真正直接向界面添加额外的行为,只有影响序列化引擎行为的额外元数据。另一方面,这个解决方案允许实现类覆盖customConverter(),这有点狡猾,标记接口不应该允许。 (那么,SerializableCloneable是否更好地依赖于实施类中的“魔术”方法?我不这么认为。)

    可以说上面的例子不是解决这类问题的好方法,你可能会更好地使用注释。但对于大多数“真正的”标记接口也是如此。

    TL;博士

    我们可以得出结论,只有默认方法的接口或多或少等同于空接口。如果你想进行理论上的区分而不是称之为标记界面,那当然没问题。但是实际上没有什么区别,考虑到标记界面的固有问题,我们应该尽量避免它们。

答案 3 :(得分:6)

标记界面可以有默认方法,但拥有它们是荒谬的。

标记界面与传统界面的使用方式不同。传统的接口定义了抽象和默认的方法。因此,程序使用该接口声明变量作为其类型,并通过该接口类型的引用来调用这两种类的方法是明智的。

通过约束,标记接口不用于调用方法。它是关于通过类型系统声明的对象的元信息。它通常通过instanceof表达式调用代码,或偶尔使用Class.isAssignableFrom()来使用。声明一个类型是标记接口的变量是没有意义的,因为对这样的变量没什么用。

JDK中标记接口的示例包括CloneableRandomAccessSerializable

现在考虑在某个标记界面添加默认方法:

interface Marker {
    default void foo() { ... }
}

foo的默认实现可以做什么?

默认方法的实现通常希望在this上运行,并且它们通过调用this上的其他实例方法来实现。他们可以调用其他默认方法,但是有一堆默认方法相互调用是没有用的。最终,必须在this上执行某种实际操作。由于接口方法无法访问状态(字段),因此任何实际操作都必须由驻留在实现类中的抽象方法实现来执行。但是,在标记界面中没有这样的方法。

foo的默认实现可以在此接口或其他类上调用静态方法。这大多没有意义,因为这样的方法首先可能更好地表达为静态方法。实现可以将this传递给静态方法,但是这个方法对这样的引用没有任何用处,因为它没有方法!好吧,它可能有默认方法,但现在我们就开始了。

对于在接口上有用的默认方法,该接口也需要具有抽象方法。但如果它有抽象方法,它就不再是标记接口。因此,在标记接口上使用默认方法是没有意义的。