标记接口与空抽象类的区别

13
我很难决定是使用一个标记接口还是一个空的抽象类。 我有两个类BrokerResponseNotification,它们没有结构上的相似之处。唯一连接它们的是需要订阅。
void register(Receivable receivable, BrokerObserver observer)

我对使用标记接口有些不喜欢,因为它违反了接口的基本定义。另一方面,使用抽象超类会让我感到不舒服,因为这两个类彼此没有关系。

在这种情况下,通常较可取的方法是什么以及为什么?

编辑1

我忘了提到BrokerResponse本身是一个抽象类,它有几个子类来确定各自的类型。


一个抽象类会强制你进入特定的继承层次结构,而接口则不会。因此,接口给你更多的自由。在这种情况下,选择抽象类可能是一个奇怪的选择。另外,考虑使用注释代替。 - khelwood
那么你的register()方法应该放在哪里呢? - Gyro Gearless
通常较好的方法是使用注释。 - Sharon Ben Asher
@SharonBenAsher,你能描述一下它是如何工作的吗? - tgr
3个回答

10
抽象类 vs. 标记接口:
标记接口没有问题,有一些使用情况。在两者之间做出选择时,标记接口更灵活。
如果要定义类型,请使用接口。
抽象类的目的是提供一个适当的超类,其他类可以从中继承,因此共享一个公共设计-你的类没有共同的设计和分享内容。此外,您将把它们都粘在某个受限制的设计中,如果将来需要为它们添加真正不同的父级,则不太灵活。
抽象类用例列表:
1. 在几个密切相关的类之间共享代码。 2. 扩展您的抽象类的类具有许多共同的方法或字段或需要除public之外的访问修饰符(例如protected和private)。 3. 声明非静态或非最终字段,这使您能够定义可以访问和修改它们所属对象状态的方法。
接口用例:
1. 不相关的类将实现您的接口。 2. 指定特定数据类型的行为,而不涉及谁实现其行为。 3. 多重继承的优点。
所有列出的参数都是用于接口的。由于BrokerResponse本身是抽象的并且具有自己的层次结构,这使得这些类没有共同之处更加强烈。
作为替代方案,您可以使用标记注释。我会考虑使用这两种方法之一来代替抽象类。
标记接口 vs. 标记注释:
根据Joshua Bloch的“Effective Java”:
标记接口比标记注释具有两个优点。首先,标记接口定义了由标记类的实例实现的类型;标记注释不会。此类型的存在使您能够在编译时捕获错误,如果使用标记注释,则只能在运行时捕获错误。标记接口比标记注释的另一个优点是它们可以更精确地定位。
何时应使用标记注释?
如果标记适用于除类或接口之外的任何程序元素,则必须使用注释,因为只有类和接口才能实现或扩展接口。
何时应使用标记接口?
问问自己,“我可能想编写一个或多个仅接受具有此标记的对象的方法吗?”如果是这样,您应该优先使用标记接口而不是注释。这将使您能够将接口用作所讨论的方法的参数类型,从而产生编译时类型检查的非常实际的好处。
总结:
如果你想定义一种没有任何新方法与之相关联的类型,标记接口是正确的选择。
如果你想标记除类和接口以外的程序元素,以便将来可以添加更多信息到标记中,或者将标记适应已经广泛使用注解类型的框架,则标记注解是正确的选择。

2
在这种情况下,使用空的抽象类没有任何意义,因为Java中不存在多重继承。 使您的类实现一些标记接口不会改变您的类层次结构,它只是用一些额外的元数据标记了您的类。
假设您的类已经被标记为Subscribable,现在又需要标记为Writable。如果使用空的抽象类,则需要重新设计整个层次结构。而使用标记接口只需将Writable添加到实现列表中即可。

2
如何为它们添加注释?如果你必须选择的话,使用标记接口是最好的方法,但根据你需要做什么来使用注释会更加清晰。你说需要让它们变得“相同”的事实表明你需要进行 instanceof 调用并基于此执行某些操作。可以通过 isAnnotationPresent 或类似方法来实现相同的效果。但是,如果你添加一个标记接口,那么如果你只需要针对有限数量的类进行测试,那么将其制作成非标记接口如何?例如:MyInterface {boolean isSubscribable();}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接