什么时候使用Java的@Override注释,为什么?


498

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

@Override注解标记每个被覆盖的方法似乎是过头的。是否有某些编程情况要求使用@Override和其他不应使用的情况@Override

Answers:


515

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

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


4
与“易于理解”相同,IDE将发现@Override批注并在编辑器中直观地标记覆盖方法。
Bob Cross

47
某些IDE会标记覆盖方法,该方法也缺少@Override批注。
Jay R.

20
另一个好处是,如果父类发生更改,则编译器将确保子类也已更新。
戴维(David)2009年

4
@Jay R .:是的。事实上,例如,Eclipse甚至可以自动添加@Override(如果缺少)。
sleske

32
如果其他人由于接口方法上的@Overrides从1.5到1.6的明显未记录的更改而到达此处,则bugs.sun.com/bugdatabase/view_bug.do?bug_id=5008260似乎是相应的bug。(感谢指出,Dave L.!)
Henrik Heimbuerger,2010年

110

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

protected boolean displaySensitiveInformation() {
  return false;
}

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

假设将此方法在父类中更改为

protected boolean displaySensitiveInformation(Context context) {
  return true;
}

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

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


46

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

编写代码时,不要过度杀伤力。键入@override不会花费任何费用,但是如果您拼写错误的方法名称或使签名略有错误,则可以节省大量资金。

这样考虑:在您浏览此处并键入此帖子时,您花费的时间几乎比一生中花费@override花费的时间多得多。但是它可以防止一个错误,从而节省您的时间。

Java会竭尽所能,确保您在编辑/编译时没有犯任何错误,这实际上是一种免费的方法,可以解决一整套错误,这些错误在综合测试之外是无法避免的。

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

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


3
“在编码时,不会太矫kill过正。” 我同意这一点,这就是为什么我发现动态langs如此错误的原因(尽管我现在100%的有偿工作都在红宝石中)。
Dan Rosenstark 2010年

4
+1:我大概有10个错误是由于覆盖错误而导致的-查找其中任何一个所需的时间很容易超过了在我的每个覆盖方法上键入@Override的时间。此外,如果@Override有点麻烦,则可能是您过度使用了继承。
劳伦斯·多尔

7
一个非常现实的缺点是,通过用蜗牛乱扔代码,使代码更难阅读。也许这是我的IDE的错,但是我自己已经经历了。
好好对待你的mods

9
@phyzome如果您发现“ Snails”很麻烦,则说明您使用的注释不够多。它们应该只是方法标头上方的一行,在大多数情况下(几行)应该与您的方法一样大,以提供体面的悬停文本和javadocs。我想我是说问题不在于蜗牛,而是您的阅读习惯。代码中的所有这些括号是否也会困扰您?
Bill K

4
是的,编码中存在过大的杀伤力:当您编写仅模仿代码显然会执行的操作的注释时。
蚂蚁2012年

22

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

它会捕获到像tostring()代替toString()

小事情对大型项目有帮助。


18

使用@Override注释可作为编译时保护措施,以防止常见的编程错误。如果您在实际上没有覆盖超类方法的方法上具有注释,则将引发编译错误。

最有用的情况是在基础类中将方法更改为具有不同的参数列表时。子类中用于覆盖超类方法的方法将不再这样做,因为方法签名已更改。这有时会导致奇怪和意外的行为,尤其是在处理复杂的继承结构时。该@Override注释保障反对这一点。


最佳答案。简短而甜美。我希望您能解释一下“保障措施”的工作原理。
djangofan 2011年

这很容易解释。如果您犯了一个错误(通过更改接口,抽象类或子类,则将收到警告(例如在Eclipse中)或编译时错误,告诉您@Override无法正常工作。实际错误消息将取决于更改的内容,但是在Eclipse中(例如)非常迅速地就知道存在问题:您会看到红色的锯齿形下划线,并且将鼠标悬停在有问题的文本上会告诉您什么地方出了问题。我称这为物有所值
Ichiro Furusato 2012年

14

要利用编译器检查的优势,您应该始终使用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注释,则会看到编译错误,并且不会浪费时间思考为什么事件处理程序未运行。


8

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

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

在1.6中,绝对不应在接口实现程序上使用@Override,并且由于eclipse可悲地选择将注释自动插入为默认行为,因此,我们得到了很多混乱的源文件。在阅读1.6代码时,您无法从@Override批注中看到某个方法是否实际上覆盖了超类中的方法或仅实现了一个接口。

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


2
关于这一点有不同的意见。参见stackoverflow.com/questions/212614/…
sleske

8

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

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

另外,如果超类更改了方法签名,则较旧的签名的覆盖可能会“孤立”,并留下混乱的死代码。该@Override注释将帮助您确定这些孤儿,让他们可以被修改,以符合新的签名。


7

如果您发现自己经常重写(非抽象)方法,则可能要看一下您的设计。当编译器否则无法捕获该错误时,这将非常有用。例如,尝试覆盖ThreadLocal中的initValue(),我已经完成了。

在实现接口方法(1.6+功能)时使用@Override对我来说有点矫kill过正。如果您有大量的方法,其中有些方法会被覆盖,而有些方法则不会,那么设计可能又很糟糕(如果您不知道,编辑器可能会显示哪个方法)。


2
实际上,对于重写的接口方法也很好。如果我从接口中删除了一个过时的旧方法,那么该方法也应该从所有实现的类中删除-如果使用@override的话,很容易发现它们。
多米尼克·桑贾贾


7

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

另外,在Joshua Block的书《 Effective Java》(第2版)中,第36项提供了有关注释好处的更多详细信息。


是的,的确-项目36 :)
克里斯·金普顿

6

在实现接口方法时,使用@Override绝对没有任何意义。在这种情况下使用它没有优势-编译器已经捕获了您的错误,因此这只是不必要的混乱。


6
使用@Override时,在界面的方法去除在接口上会强迫你通知。
Alex B 2010年

@Alex:删除接口中的方法是一项重大更改,就像添加它们一样。接口发布后,除非您完全控制使用该接口的所有代码,否则该接口将被有效锁定。
劳伦斯·多尔

6

每当一个方法重写另一个方法,或者某个方法在接口中实现签名时。

@Override注释可确保您确实覆盖了某些内容。如果没有注释,则可能会拼写错误或参数类型和数量有所不同。


1
您只能使用它来标记Java 1.6
Dave L中的

5

我每次都用它。在一年中重新访问代码时,我可以使用它来快速了解发生了什么,而我已经忘记了第一次的想法。


5

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

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

拥有这样的安全网永远都是好事。


1
因此,如果更改了父方法,并且未在子类的方法中使用@Override,则编译器会说什么还是保持沉默?使用“替代”是否可以为您提供更多信息?
djangofan 2011年

5

我到处都用它。关于标记方法的工作,我让Eclipse为我做,因此,这无需额外的工作。

我信奉持续重构...。因此,我将尽一切努力使它更加顺利。


5
  • 仅用于方法声明。
  • 指示带注释的方法声明将覆盖超类型中的声明。

如果持续使用,它将保护您免受各种恶意错误的侵害。

使用@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());
    }
}

资料来源:有效的Java


我不知道Java中的运算符优先级规则是什么,但是您的equals方法正在尖叫BUUUUUUUUUUUUG!我会写(b.first == first) && (b.second == second),即使&&优先级低于==
2009年

您是否知道您的链接显示了一个“您必须订阅”消息,覆盖了该页面的有用部分?
09年

@Adriano:对不起,伙计!我很无奈!当我写“答案”时,它可用。不用担心..买书。值得拥有它!
JAI

5
equals方法不会覆盖:原始方法Object::equalsboolean equals(Object),而被覆盖的方法equalsboolean equals(Bigram),具有不同的方法签名,因此不会覆盖。将@Override添加到equals会检测到此错误。
Ming-Tang


2

似乎这里的智慧正在改变。今天,我安装了IntelliJ IDEA 9,并注意到它的“ 缺少@Override检查 ”现在不仅捕获实现的抽象方法,还捕获实现的接口方法。在我的雇主的代码库和我自己的项目中,我长期以来习惯只对前一种方法使用@Override -实现抽象方法。但是,重新考虑这种习惯,在两种情况下使用注释的好处就变得很明显。尽管更为冗长,但它确实可以防止接口方法名称更改的脆弱基类问题(不像C ++相关示例那样严重),在派生类中孤立了将要实现的方法。

当然,这种情况大都是夸张的。派生类将不再编译,现在缺少重命名的接口方法的实现,如今,人们可能会使用“ 重命名方法”重构操作来解决整个代码库。

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


2

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

顺便说一句,如果我们没有在子类中声明注解@Override,但是我们确实重写了super的某些方法,则该函数可以与@Override一起使用。但是,当更改超级方法时,此方法无法通知开发人员。因为它不知道开发者的目的-覆盖super的方法还是定义新的方法?

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


1

我尽可能地使用它来标识何时重写方法。如果您查看Scala编程语言,它们还具有override关键字。我觉得这很有用。


0

当您在要覆盖的方法名称上使用了错误的拼写时,它的确允许您(很好,编译器)捕获。


0

覆盖注释用于利用编译器的优势,用于检查您是否实际上是从父类覆盖方法。它用于通知您是否犯了任何错误,例如拼写错误的方法名称,不正确匹配参数的错误。


0

我认为最好在允许的情况下对@override进行编码。它有助于编码。但是,需要注意的是,对于密钥Helios(sdk 5或6),允许使用已实现的接口方法的@override注释。对于Galileo,无论是5还是6,都不允许@override注释。


0

批注确实向编译器提供了有关代码的元数据,并且当我们重写基类的任何方法时,在继承的情况下使用批注@Override。它只是告诉编译器您正在重写方法。它可以避免某些常见错误,例如我们不遵循方法的正确签名或方法名称中的拼写错误等。因此,使用@Override注释是一种很好的做法。


0

对我来说,@ Override确保我拥有正确的方法签名。如果我在批注中添加了该方法,并且该方法的拼写不正确,则编译器会抱怨让我知道某些错误。


0

简单–当您要覆盖超类中存在的方法时,请使用@Override注释进行正确的覆盖。如果您没有正确覆盖它,编译器会警告您。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.