我认为它不能,因为标记接口原则是没有任何方法,但由于默认方法不是 abstract 我不确定。
答案 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是编写的。
有两种查看标记接口的方法:
“官方”定义是第一个,但直到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()
,这有点狡猾,标记接口不应该允许。 (那么,Serializable
和Cloneable
是否更好地依赖于实施类中的“魔术”方法?我不这么认为。)
可以说上面的例子不是解决这类问题的好方法,你可能会更好地使用注释。但对于大多数“真正的”标记接口也是如此。
我们可以得出结论,只有默认方法的接口或多或少等同于空接口。如果你想进行理论上的区分而不是称之为标记界面,那当然没问题。但是实际上没有什么区别,考虑到标记界面的固有问题,我们应该尽量避免它们。
答案 3 :(得分:6)
标记界面可以有默认方法,但拥有它们是荒谬的。
标记界面与传统界面的使用方式不同。传统的接口定义了抽象和默认的方法。因此,程序使用该接口声明变量作为其类型,并通过该接口类型的引用来调用这两种类的方法是明智的。
通过约束,标记接口不用于调用方法。它是关于通过类型系统声明的对象的元信息。它通常通过instanceof
表达式调用代码,或偶尔使用Class.isAssignableFrom()
来使用。声明一个类型是标记接口的变量是没有意义的,因为对这样的变量没什么用。
JDK中标记接口的示例包括Cloneable
,RandomAccess
和Serializable
。
现在考虑在某个标记界面添加默认方法:
interface Marker {
default void foo() { ... }
}
foo
的默认实现可以做什么?
默认方法的实现通常希望在this
上运行,并且它们通过调用this
上的其他实例方法来实现。他们可以调用其他默认方法,但是有一堆默认方法相互调用是没有用的。最终,必须在this
上执行某种实际操作。由于接口方法无法访问状态(字段),因此任何实际操作都必须由驻留在实现类中的抽象方法实现来执行。但是,在标记界面中没有这样的方法。
foo
的默认实现可以在此接口或其他类上调用静态方法。这大多没有意义,因为这样的方法首先可能更好地表达为静态方法。实现可以将this
传递给静态方法,但是这个方法对这样的引用没有任何用处,因为它没有方法!好吧,它可能有默认方法,但现在我们就开始了。
对于在接口上有用的默认方法,该接口也需要具有抽象方法。但如果它有抽象方法,它就不再是标记接口。因此,在标记接口上使用默认方法是没有意义的。