你啥时候使用 Java 的 @Override 注解,为啥?

Posted

技术标签:

【中文标题】你啥时候使用 Java 的 @Override 注解,为啥?【英文标题】:When do you use Java's @Override annotation and why?你什么时候使用 Java 的 @Override 注解,为什么? 【发布时间】:2010-09-10 19:07:29 【问题描述】:

使用 Java 的 @Override 注释的最佳实践是什么?为什么?

@Override 注解标记每一个被覆盖的方法似乎有点过头了。是否存在需要使用@Override 的某些编程情况以及不应该使用@Override 的其他情况?

【问题讨论】:

【参考方案1】:

我每次都用它。当我在一年后重新访问代码并且忘记了我第一次在想什么时,可以使用它来快速弄清楚发生了什么。

【讨论】:

【参考方案2】:

我总是使用标签。这是一个简单的编译时标志,用于捕捉我可能犯的小错误。

它将捕获 tostring() 而不是 toString() 之类的内容

小事对大项目有帮助。

【讨论】:

【参考方案3】:

每当一个方法覆盖另一个方法,或一个方法在接口中实现签名时。

@Override 注释向您保证您确实覆盖了某些内容。如果没有注释,您可能会出现拼写错误或参数类型和编号不同的风险。

【讨论】:

在Java 1.6中只能用来标记接口实现【参考方案4】:

它确实允许您(好吧,编译器)在您对要覆盖的方法名称使用错误拼写时进行捕捉。

【讨论】:

【参考方案5】:

每次重写方法时使用它有两个好处。这样做是为了您可以利用编译器检查来确保您实际上正在覆盖您认为的方法。这样,如果您犯了拼写错误的方法名称或未正确匹配参数的常见错误,您将被警告您的方法实际上并没有像您认为的那样覆盖。其次,它使您的代码更容易理解,因为当方法被覆盖时会更加明显。

此外,在 Java 1.6 中,您可以使用它来标记方法何时实现接口以获得相同的好处。我认为最好有一个单独的注解(如@Implements),但总比没有好。

【讨论】:

与“更容易理解”一样,IDE 将发现 @Override 注释并在编辑器中直观地标记覆盖方法。 一些 IDE 会标记一个也缺少 @Override 注释的覆盖方法。 另一个好处是,如果父类发生变化,编译器会确保子类也已更新。 @Jay R.:是的。事实上,例如如果 @Override 缺失,Eclipse 甚至可以自动添加它。 如果其他人因为来自接口的方法的 @Overrides 从 1.5 到 1.6 的明显未记录更改而来到这里,bugs.sun.com/bugdatabase/view_bug.do?bug_id=5008260 似乎是相应的错误。 (感谢您指出,Dave L.!)【参考方案6】:

使用@Override 注释可以在编译时防止常见的编程错误。如果你在一个方法上有注解,你实际上并没有覆盖超类方法,它将引发编译错误。

这很有用的最常见情况是当您更改基类中的方法以具有不同的参数列表时。由于更改了方法签名,用于覆盖超类方法的子类中的方法将不再这样做。这有时会导致奇怪和意外的行为,尤其是在处理复杂的继承结构时。 @Override 注释可以防止这种情况发生。

【讨论】:

最佳答案。短而甜。我希望你能解释一下“保障”是如何工作的……没有人解释过。 解释起来很简单。如果您犯了错误(通过更改接口、抽象类或子类,您将收到警告(例如在 Eclipse 中)或编译时错误,告诉您 @Override 不起作用。实际错误消息将取决于更改的内容,但在 Eclipse(例如)中,很明显很快就会出现问题:您会看到红色的小锯齿形下划线,并且将鼠标悬停在有问题的文本上会告诉您出了什么问题。我称之为物超所值。【参考方案7】:

如果您发现自己经常覆盖(非抽象)方法,您可能想看看您的设计。当编译器不会捕获错误时,它非常有用。例如尝试覆盖 ThreadLocal 中的 initValue(),我已经这样做了。

在实现接口方法(1.6+ 功能)时使用@Override 对我来说似乎有点矫枉过正。如果你有很多方法,其中一些覆盖而另一些没有,那可能又是糟糕的设计(如果你不知道,你的编辑器可能会显示哪个是哪个)。

【讨论】:

其实,重写接口方法也不错。如果我例如从接口中删除一个旧的、不推荐使用的方法,该方法也应该从所有实现类中删除——如果它们使用@override,很容易发现它们。【参考方案8】:

最好的做法是始终使用它(或让 IDE 为您填充它们)

@Override 的用途是检测父类中尚未报告到层次结构中的更改。 没有它,您可以更改方法签名而忘记更改其覆盖,使用@Override,编译器会为您捕获它。

拥有这种安全网总是好的。

【讨论】:

那么,如果你改变了父方法,并且你没有在子类的方法中使用@Override,编译会说什么还是保持沉默?使用“覆盖”会为您提供更多信息吗?如果是,是什么?【参考方案9】:

我认为它作为编译时提醒最有用的是该方法的意图是覆盖父方法。举个例子:

protected boolean displaySensitiveInformation() 
  return false;

您经常会看到类似上述方法的内容,它覆盖了基类中的方法。这是该类的一个重要实现细节——我们不希望显示敏感信息。

假设这个方法在父类中改为

protected boolean displaySensitiveInformation(Context context) 
  return true;

此更改不会导致任何编译时错误或警告 - 但它会完全改变子类的预期行为。

回答您的问题:如果在超类中缺少具有相同签名的方法表明存在错误,则应使用 @Override 注释。

【讨论】:

【参考方案10】:

接口上的@Override 实际上很有帮助,因为如果您更改接口,您会收到警告。

【讨论】:

【参考方案11】:

我在任何地方都使用它。 关于标记方法的工作,我让 Eclipse 为我做,所以没有额外的工作。

我对持续重构很感兴趣......所以,我会用每一件小事让它更顺利。

【讨论】:

【参考方案12】:

最好将它用于每个打算作为覆盖的方法,以及 Java 6+,每个打算作为接口实现的方法。

首先,它会在编译时捕获像“hashcode()”这样的拼写错误,而不是“hashCode()”。当真正的原因是您的代码从未被调用时,调试为什么您的方法的结果似乎与您的代码不匹配可能会令人费解。

此外,如果超类更改了方法签名,则旧签名的覆盖可能会“孤立”,留下令人困惑的死代码。 @Override 注释将帮助您识别这些孤儿,以便可以修改它们以匹配新签名。

【讨论】:

【参考方案13】:

这里有很多好的答案,所以让我提供另一种方式来看待它......

编码时没有矫枉过正。键入 @override 不会花费您任何费用,但是如果您拼错了方法名称或签名稍有错误,则可以节省大量资金。

这样想:在你导航到这里并输入这篇文章的时间里,你所用的时间比你在余生中输入@override 所花费的时间要多得多;但它防止的一个错误可以为您节省数小时。

Java 尽其所能确保您在编辑/编译时没有犯任何错误,这是一种几乎免费的方法来解决除了综合测试之外的任何其他方式都无法避免的整类错误.

您能否在 Java 中提出一种更好的机制来确保当用户打算覆盖一个方法时,他确实做到了?

另一个巧妙的效果是,如果您不提供注释,它会在编译时警告您不小心覆盖了父方法 - 如果您不打算这样做,这可能很重要。

【讨论】:

“编码时没有矫枉过正。”我同意这一点,这就是为什么我发现动态语言如此错误的原因(尽管我现在 100% 的付费工作都是用 ruby​​ 编写的)。 +1:我可能有 10 个由覆盖错误引起的错误 - 找到其中任何一个所需的时间很容易超过在我的每一个上键入 @Override 的时间压倒一切的方法。此外,如果@Override 是一些负担,你可能过度使用继承。 一个非常实际的缺点是,你会因为乱扔蜗牛而使代码更难阅读。也许这是我的 IDE 的问题,但我自己也经历过。 @phyzome 如果您发现“蜗牛”很麻烦,那么您没有使用足够多的 cmets。它们应该只是您的方法标题上方的单行,在大多数情况下(几行)应该与您的方法一样大,以提供体面的悬停文本和 javadocs。我想我是说问题不是蜗牛,而是你的阅读习惯。代码中的所有括号是否也困扰着您? 是的,编码有点矫枉过正:当你编写的 cmets 只是模仿代码明显所做的事情。【参考方案14】:

它所做的另一件事是,它在阅读代码时更明显地表明它正在改变父类的行为。比可以帮助调试。

此外,在 Joshua Block 的《Effective Java (2nd edition)》一书中,第 36 项详细介绍了注释的好处。

【讨论】:

【参考方案15】: interface implementation 上的

@Override 不一致,因为在 java 中没有“覆盖接口”之类的东西。

interface implementation 上的

@Override 是无用的,因为在实践中它不会捕获编译无论如何都不会捕获的错误。 只有一种牵强附会的场景,实现者的覆盖实际上做了一些事情:如果您实现了一个接口,并且接口 REMOVES 方法,您将在编译时收到通知,您应该删除未使用的实现。请注意,如果新版本的接口具有 NEW 或 CHANGED 方法,那么您显然会得到一个编译错误,因为您没有实现新的东西。

接口实现者上的@Override 在 1.6 中不应该被允许,而且 Eclipse 遗憾地选择自动插入注释作为默认行为,我们得到了很多杂乱的源文件。在阅读 1.6 的代码时,您无法从 @Override 注解中看到一个方法实际上是覆盖了超类中的方法还是只是实现了一个接口。

在实际覆盖超类中的方法时使用@Override 很好。

【讨论】:

关于这一点有不同的看法。见***.com/questions/212614/…。【参考方案16】:

要利用编译器检查,您应该始终使用 Override 注释。但不要忘记,Java Compiler 1.5 在重写接口方法时不允许使用此注解。您可以使用它来覆盖类方法(抽象或非抽象)。

某些 IDE,如 Eclipse,即使配置了 Java 1.6 运行时或更高版本,它们仍保持与 Java 1.5 的合规性,并且不允许如上所述使用 @override。要避免这种行为,您必须转到:项目属性 -> Java 编译器 -> 选中“启用项目特定设置” -> 选择“编译器合规级别”= 6.0 或更高。

如果基础是接口或类,我喜欢在每次独立覆盖方法时使用此注解。

这可以帮助您避免一些典型的错误,例如当您认为您正在覆盖事件处理程序时,您会发现什么也没有发生。想象一下,你想为某个 UI 组件添加一个事件监听器:

someUIComponent.addMouseListener(new MouseAdapter()
  public void mouseEntered() 
     ...do something...
  
);

上面的代码编译并运行,但是如果你在 someUIComponent 中移动鼠标,“做某事”的代码将会运行,因为实际上你并没有覆盖基本方法mouseEntered(MouseEvent ev)。您只需创建一个新的无参数方法mouseEntered()。如果您使用了 @Override 注释而不是该代码,那么您会看到编译错误,并且您没有浪费时间思考为什么您的事件处理程序没有运行。

【讨论】:

【参考方案17】: 仅用于方法声明。 表示注解的方法 声明覆盖声明 在超类型中。

如果始终如一地使用,它可以保护您免受一大类恶意错误的侵害。

使用@Override 注解来避免这些错误: (找出以下代码中的错误:)

public class Bigram 
    private final char first;
    private final char second;
    public Bigram(char first, char second) 
        this.first  = first;
        this.second = second;
    
    public boolean equals(Bigram b) 
        return b.first == first && b.second == second;
    
    public int hashCode() 
        return 31 * first + second;
    

    public static void main(String[] args) 
        Set<Bigram> s = new HashSet<Bigram>();
        for (int i = 0; i < 10; i++)
            for (char ch = 'a'; ch <= 'z'; ch++)
                s.add(new Bigram(ch, ch));
        System.out.println(s.size());
    

来源:Effective Java

【讨论】:

我不知道Java中的运算符优先级规则是什么,但是您的equals方法正在尖叫BUUUUUUUUUUUG!我会写(b.first == first) &amp;&amp; (b.second == second),即使&amp;&amp; 的优先级低于== 您是否知道您的链接显示“您必须订阅”消息,涵盖该页面的有用部分? @Adriano:对不起老兄!!很无奈!!当我写下“答案”时,它是可用的。不用担心..买书。值得拥有!! equals方法不覆盖:原来的Object::equalsboolean equals(Object),而覆盖的equalsboolean equals(Bigram),有不同的方法签名,不覆盖。将@Override 添加到equals 将检测到这个错误。【参考方案18】:

看来这里的智慧正在改变。今天我安装了IntelliJ IDEA 9 并注意到它的“missing @Override inspection”现在不仅捕获了实现的抽象方法,还捕获了实现的接口方法。在我雇主的代码库和我自己的项目中,我长期以来一直习惯于只对前者使用 @Override —— 实现了抽象方法。然而,重新思考这个习惯,在这两种情况下使用注释的优点变得清晰起来。尽管更加冗长,但它确实可以防止接口方法名称发生更改的脆弱的基类问题(不像与 C++ 相关的示例那么严重),在派生类中孤立可能实现的方法。

当然,这个场景大多是夸张的;派生类将不再编译,现在缺少重命名接口方法的实现,今天可能会使用重命名方法重构操作来处理整个代码库。

鉴于 IDEA 的检查无法配置为忽略已实现的接口方法,今天我将改变我的习惯和团队的代码审查标准。

【讨论】:

【参考方案19】:

简单——当你想覆盖超类中的方法时,使用@Override注解进行正确的覆盖。如果你没有正确覆盖它,编译器会警告你。

【讨论】:

【参考方案20】:

在实现接口方法时使用@Override 绝对没有意义。在这种情况下使用它没有任何好处——编译器已经发现了你的错误,所以它只是不必要的混乱。

【讨论】:

在接口上使用@Override 会强制您注意到接口中的方法何时被删除。 @Alex:删除接口中的方法是一项重大更改,就像添加它们一样。接口发布后,除非您完全控制使用它的所有代码,否则它会被有效锁定。【参考方案21】:

在使用 Override 时要小心,因为之后不能在 starUML 中进行逆向工程;先做 uml。

【讨论】:

【参考方案22】:

Override 注解用于利用编译器,检查您是否真的覆盖了父类中的方法。用于通知方法名拼写错误、参数匹配不正确等错误

【讨论】:

【参考方案23】:

我认为最好在允许的情况下对@override 进行编码。它有助于编码。但是,需要注意的是,对于 ecipse Helios,无论是 sdk 5 还是 6,都允许实现接口方法的 @override 注释。至于伽利略,无论是5还是6,都不允许@override注解。

【讨论】:

【参考方案24】:

我尽可能多地使用它来识别方法何时被覆盖。如果您查看 Scala 编程语言,它们也有一个 override 关键字。我觉得它很有用。

【讨论】:

【参考方案25】:

注解确实为编译器提供了有关代码的元数据,并且注解@Override 用于在我们覆盖基类的任何方法时进行继承。它只是告诉编译器您正在覆盖方法。它可以避免我们可以做的一些常见错误,例如不遵循正确的方法签名或方法名称拼写错误等。因此使用@Override注解是一个好习惯。

【讨论】:

【参考方案26】:

注解@Override 用于帮助检查开发人员是否在父类或接口中重写了正确的方法。当 super 的方法名称发生变化时,编译器会通知这种情况,这只是为了保持与 super 和子类的一致性。

顺便说一句,如果我们没有在子类中声明注解@Override,但我们确实覆盖了父类的某些方法,那么该函数可以与@Override 一样工作。但是当super的方法改变时,这个方法不能通知开发者。因为它不知道开发者的目的——重写super的方法还是定义一个新的方法?

所以当我们想要重写该方法以利用多态性时,我们最好在方法上方添加@Override。

【讨论】:

【参考方案27】:

对我来说,@Override 确保我拥有正确的方法签名。如果我输入了注释并且方法拼写不正确,那么编译器会抱怨让我知道有问题。

【讨论】:

以上是关于你啥时候使用 Java 的 @Override 注解,为啥?的主要内容,如果未能解决你的问题,请参考以下文章

你啥时候在 Java 中使用 volatile 关键字? [复制]

你啥时候想在 C++ 中使用指针和值?

你啥时候使用“受保护的内部”访问修饰符?

你啥时候使用“this”关键字? [关闭]

你啥时候在流中使用接口而不是类型别名?

你啥时候会使用不同的 git 合并策略?