什么时候使用Java的@Override注释,为什么?
使用Java的@Override注释的最佳实践是什么,为什么?
用@Override注释标记每个被覆盖的方法似乎是多余的。是否有某些编程情况要求使用@Override,而另一些则永远不要使用@Override?
解决方案
我每次都用它。在一年中重新访问代码时,我可以使用它来快速了解发生了什么,而我已经忘记了第一次的想法。
我总是使用标签。这是一个简单的编译时标志,用于捕获我可能犯的小错误。
它将捕获诸如tostring()
而不是toString()
之类的东西
小事情对大型项目有帮助。
每当一个方法重写另一个方法,或者某个方法在接口中实现签名时。
@ Override
注释可确保我们确实覆盖了某些内容。如果没有注释,则可能会拼写错误或者参数类型和数量有所不同。
当我们对要覆盖的方法名称使用了错误的拼写时,它确实使我们(编译器)可以捕获。
每次我们重写一种方法都有两个好处时使用它。这样做是为了使我们能够利用编译器检查的优势,以确保我们认为自己确实覆盖了某个方法。这样,如果我们犯了拼写错误的方法名称或者不正确匹配参数的常见错误,则会警告我们方法实际上并未像我们认为的那样覆盖。其次,它使代码更易于理解,因为当方法被覆盖时,它更加明显。
另外,在Java 1.6中,我们可以使用它来标记方法实现相同好处的接口的时间。我认为最好有一个单独的注释(如@Implements),但总比没有好。
使用@ Override
注释可以防止常见的编程错误。如果我们在实际上未覆盖超类方法的方法上具有注释,则会引发编译错误。
最有用的情况是在基础类中将方法更改为具有不同的参数列表时。子类中用于重写超类方法的方法由于更改了方法签名而不再这样做。这有时会导致奇怪和意外的行为,尤其是在处理复杂的继承结构时。 @Override注释可以防止这种情况。
如果我们发现自己经常重写(非抽象)方法,则可能要看一下设计。当编译器否则无法捕获该错误时,这将非常有用。例如,尝试覆盖ThreadLocal中的initValue(),我已经完成了。
在实现接口方法(1.6+功能)时使用@Override对我来说有点过头了。如果我们有大量的方法,其中有些方法会被覆盖而有些方法则没有,那么这可能又是糟糕的设计(如果我们不知道,编辑器可能会显示哪个方法)。
最好的方法是始终使用它(或者让IDE为我们填充它们)
@Override的用途是检测尚未在层次结构中报告的父类中的更改。
没有它,我们可以更改方法签名,而忘记更改其覆盖,使用@Override,编译器会为我们捕获它。
那种安全网永远都是好东西。
我认为这是最有用的作为编译时提示,该方法的意图是重写父方法。举个例子:
protected boolean displaySensitiveInformation() { return false; }
我们经常会看到类似上述方法的内容,该方法会覆盖基类中的方法。这是此类的重要实现细节-我们不希望显示敏感信息。
假设将此方法在父类中更改为
protected boolean displaySensitiveInformation(Context context) { return true; }
此更改不会导致任何编译时错误或者警告,但会完全更改子类的预期行为。
要回答问题:如果在超类中缺少具有相同签名的方法表明存在错误,则应使用@Override批注。
实际上,在接口上使用@Override很有帮助,因为如果更改接口,则会收到警告。
我到处都用它。
关于标记方法的工作,我让Eclipse为我做,因此,这无需额外的工作。
我信奉持续重构...。因此,我将尽一切努力使它更顺利地进行。
最好将其用于打算用作覆盖的每种方法,以及Java 6+,打算用作接口实现的每种方法。
首先,它在编译时捕获了类似"hashcode()
"而不是"hashCode()
"的拼写错误。当真正的原因是永远不会调用代码时,调试为什么方法的结果似乎与代码不匹配可能令人困惑。
同样,如果超类更改了方法签名,则较旧的签名的覆盖可能会"孤立",并留下混乱的死代码。 @Override注解将识别这些孤儿,以便可以对其进行修改以匹配新签名。
这里有很多很好的答案,所以让我提供另一种看待它的方法...
编写代码时,不要太矫kill过正。键入@override不会花费任何费用,但是如果我们拼写错误的方法名称或者使签名略有错误,则可以节省大量资金。
这样想:在我们浏览此处并键入此帖子时,我们花费的时间几乎比一生中花费@override花费的时间多得多;但它避免的一个错误可以节省时间。
Java会竭尽所能,以确保我们在编辑/编译时没有犯任何错误,这实际上是一种免费的方法,可以解决一整套错误,这些错误在全面测试之外是无法避免的。
我们能否在Java中提出一种更好的机制,以确保当用户打算重写某个方法时,他确实做到了?
另一个很好的效果是,如果不提供注释,它将在编译时警告我们不小心覆盖了父方法-如果我们不打算这样做,那么这可能很重要。
它所做的另一件事是,在阅读代码时,它正在更改父类的行为,从而使其变得更加明显。比可以帮助调试。
另外,在Joshua Block的书《 Effective Java》(第二版)中,第36项提供了有关注释好处的更多详细信息。
接口实现上的@Override是不一致的,因为在Java中没有"覆盖接口"之类的东西。
@Override在接口实现上是没有用的,因为在实践中它不会捕获编译无论如何都不会捕获的错误。
只有一种牵强附会的情况,实现者的重写实际上会做一些事情:如果我们实现一个接口,并且该接口使用REMOVES方法,则会在编译时通知我们应该删除未使用的实现。请注意,如果新版本的接口具有NEW或者CHANGED方法,则由于我们未实现新的东西而显然仍然会遇到编译错误。
在1.6中,绝对不应在接口实现程序上使用@Override,并且由于eclipse可悲地选择将注释自动插入为默认行为,因此我们得到了很多混乱的源文件。在阅读1.6代码时,我们无法从@Override注释中看到某个方法是否实际上覆盖了超类中的方法或者仅实现了一个接口。
在实际重写超类中的方法时使用@Override很好。
为了利用编译器检查的优势,我们应该始终使用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注释,则会看到编译错误,而我们并没有在浪费时间思考为什么事件处理程序未运行。
- 仅用于方法声明。
- 指示带注释的方法声明将覆盖超类型中的声明。
如果持续使用,它将保护我们免受各种恶意错误的侵害。
使用@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
似乎这里的智慧正在改变。今天,我安装了IntelliJ IDEA 9,并注意到它的"缺少@Override检查"现在不仅捕获了实现的抽象方法,还捕获了实现的接口方法。在我的雇主的代码库和我自己的项目中,我早就习惯只对以前实现的抽象方法使用@Override。但是,重新考虑这种习惯,在两种情况下使用注释的优点就变得很明显。尽管更为冗长,但它确实可以防止接口方法名称更改的脆弱基类问题(不像C ++相关示例那样严重),从而在派生类中孤立了可能的实现方法。
当然,这种情况大都是夸张的。派生类将不再编译,现在缺少重命名的接口方法的实现,如今,人们可能会使用"重命名方法"重构操作来批量处理整个代码库。
鉴于无法将IDEA的检查配置为忽略已实现的接口方法,因此今天我将改变我的习惯和团队的代码审查标准。
很简单,当我们要覆盖超类中存在的方法时,请使用@Override注解进行正确的覆盖。如果我们没有正确覆盖它,编译器会警告我们。
在实现接口方法时,使用@Override绝对没有任何意义。在这种情况下使用它没有优势-编译器已经捕获了错误,因此这只是不必要的混乱。
使用Override时要小心,因为之后不能在starUML中进行反向工程;首先使uml。
重写批注用于利用编译器的优势,用于检查我们是否实际上是从父类重写方法。它用于通知我们是否犯了任何错误,例如拼写错误的方法名称,不正确匹配参数的错误。
我认为最好在允许的情况下对@override进行编码。它有助于编码。但是,需要注意的是,对于密钥Helios(sdk 5或者6),允许使用已实现接口方法的@override注释。对于Galileo,无论是5还是6,都不允许@override注释。