14

我的问题是,如果通过扩展已经实现它的类隐式实现的接口应该由该类显式实现,如果该类想要宣传这样一个事实,即它履行了该接口的合同。

例如,如果你想编写一个类,它会满足接口的约定java.util.List。你实现这个,扩展java.util.AbstractList已经实现接口的类List。您是否明确声明您实现了 List?

public class MyList extends AbstractList implements List

或者您是否使用隐式方式节省打字?

public class MyList extends AbstractList

哪种方式被认为是更好的风格?你有什么理由偏爱一种或另一种方式?在哪些情况下您更喜欢方式 1 或方式 2?

4

12 回答 12

13

避免冗余。使用方法2。

使用 @Override 进行覆盖。

于 2008-12-12T13:47:27.900 回答
9

这不是一个“风格”问题。如果要扩展已经实现 List 的 AbstractList,则不需要显式实现 List。

这不仅仅是节省打字的问题。由于额外的“实现列表”是多余的,有人可能会花一些时间试图弄清楚它为什么存在。你本质上是在写一个已经是语言一部分的保证。

于 2008-12-12T13:48:31.050 回答
4

我很久以前在我的博客上问过同样的问题。如果您有兴趣了解其他人的想法,那里也有很长的讨论。有趣的是,这两种策略都是在 JDK 中采用的。

我最终决定,在这方面的硬性规则没有意义——最好对我想要传达的内容做出最佳判断。

于 2008-12-13T04:07:09.913 回答
3

如果您关心您的类的用户能够通过 Class.getInterfaces() 获取已实现接口的列表,那么您的类应该在其实现列表中提及所有接口。Class.getInterfaces() 不会返回超类实现的接口。以下程序将打印 1,然后打印 0。

import java.io.Serializable;

class Test implements Serializable {
    class Inner extends Test {
    }

    public static void main(String[] argv) throws Exception {
        System.out.println(Test.class.getInterfaces().length);
        System.out.println(Inner.class.getInterfaces().length);
    }
}
于 2009-02-12T00:25:36.437 回答
1

我会说更逐字的方式。为什么要让其他阅读您的代码的人必须查找类实现的内容?它清楚地说明了您的类的继承功能。但我会尽量保持项目中所有代码的一致性。如果你只在一个地方做这将是令人困惑的,但如果你一直这样做,他们就会意识到冗余。

旁注:Java 中有这么多类,谁知道它们?更何况,谁知道每个类从哪些类实现?你多打字几秒钟就可以为其他开发人员节省一两分钟的看课时间。

于 2008-12-12T13:55:21.520 回答
1

其他评论者说你应该避免冗余,我同意他们的观点。

为了回答一般性问题,我能想到的同时说明两者的唯一情况是,如果您扩展(比如说)AbstractFrotzer 并实现了 Frobner,AbstractFrotzer 实现了 Frobner,但您有理由相信它可能不会在未来发生。如果 AbstractFrotzer 是真正抽象的,那么它甚至可能不会实现 Frobner 的所有方法本身。这意味着某人可以更改 AbstractFrotzer 的合同,而无需更改您的类或任何其他依赖于您的类的代码成为 Frobner。但是,我认为这是一种非常罕见的情况,即使它发生并且您的类只扩展了 AbstractFrotzer,也很容易对其进行快速语义检查,并在必要时向其中添加 implements 子句那一点。

于 2008-12-12T15:43:25.047 回答
1

虽然我通常坚持选项 2,但我会坚持认为选项 1 在适用的更多情况下实际上是合适的。

代码的可用性和可理解性很重要。我们必须问自己的问题是,使用或看到对此类的引用的开发人员是否会直观地理解该类实现了该接口(因此本质上是一个子类型)。

在典型情况下编写良好的类中,类的名称应该清楚地表明它实现了接口。添加接口只是一个冗余。

但是,如果类名不能清楚地表明它实现了哪些接口,并且尽管有异味,事情就是这样,而且这是会卡住的名称,那么添加实现以明确指示接口是有意义的. 如果将来有人更改层次结构,这也很好,这在像这样不直观的继承中是可能的。

于 2008-12-13T22:15:35.767 回答
1

虽然在子类中实现接口是多余的,但实现它是一个好习惯。因为有人可能会从超类中删除已实现的接口。

于 2014-09-30T14:53:50.670 回答
1

如上面的答案所述,无需明确执行此操作。

但有时,你神秘地不得不这样做。我有一些库,其中包含用于 JIRA 插件开发的类和类:

public abstract class Condition extends AbstractWebCondition

实现失败,直到我在类声明中明确添加“实现 com.atlassian.plugin.web.Condition”(尽管AbstractWebCondition确实实现了它)。幸运的是在这里找到了解决方案。

我不知道这种行为的起源,但我们到了。从 JIRA 7.0.0 更新到 JIRA 7.13.18 时观察到。

于 2021-07-07T11:37:05.673 回答
0

当您扩展 AbstractList 时,MyList 已经属于“类型”List,因此无需显式统计(或实现)接口。

于 2008-12-12T13:50:07.037 回答
0

作为一般规则,使用选项 2,而不是选项 1。

在绝大多数情况下,选项 1 不会为开发人员或将要维护代码的人提供任何附加值。

如果有人真的想知道某个特定类的所有根源,那么任何 IDE 都应该能够为您做到这一点,直截了当。(Eclipse: ctrl+shift+G)

如果 AbstractList 决定不再实现 List 怎么办?绝大多数普通班级不会发生这种情况。其他不太明显(不太值得信赖)的呢?当然也有例外,但很少(<1%?)

于 2008-12-13T22:09:37.790 回答
0

正如你们中的许多人已经指出的那样,我以前见过这种情况,但在技术上感觉并不正确。我认为这应该是显而易见的,但是如果由于某种原因不是我认为通过类注释指示继承而不是再次显式实现接口更有意义。

如果基类实现了多个接口怎么办?然后,您是否显式实现所有这些接口,并一直备份继承链?显然不是,所以我认为这种做法的不一致证明自己是错误的。可以通过多种方式传达意图,恕我直言,这不是最好的方法。

于 2009-02-11T21:35:49.713 回答