Item 40: Consistently use the Override annotation(坚持使用 @Override 注解)

The Java libraries contain several annotation types. For the typical programmer, the most important of these is @Override. This annotation can be used only on method declarations, and it indicates that the annotated method declaration overrides a declaration in a supertype. If you consistently use this annotation, it will protect you from a large class of nefarious bugs. Consider this program, in which the class Bigram represents a bigram, or ordered pair of letters:

Java 库包含几种注解类型。对于大多数的程序员来说,其中最重要的是 @Override。此注解只能在方法声明上使用,带有该注解的方法声明将覆盖超类型中的声明。如果你坚持使用这个注解,它将帮助你减少受到有害错误的影响。考虑这个程序,其中类 Bigram 表示一个二元语法,或有序的字母对:

// Can you spot the bug?
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<>();

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

The main program repeatedly adds twenty-six bigrams, each consisting of two identical lowercase letters, to a set. Then it prints the size of the set. You might expect the program to print 26, as sets cannot contain duplicates. If you try running the program, you’ll find that it prints not 26 but 260. What is wrong with it?

主程序重复地向一个集合中添加 26 个 bigram,每个 bigram 由两个相同的小写字母组成。然后它打印该集合的大小。如果你尝试运行该程序,你会发现它打印的不是 26 而是 260。有什么问题吗?

Clearly, the author of the Bigram class intended to override the equals method (Item 10) and even remembered to override hashCode in tandem (Item 11). Unfortunately, our hapless programmer failed to override equals, overloading it instead (Item 52). To override Object.equals, you must define an equals method whose parameter is of type Object, but the parameter of Bigram’s equals method is not of type Object, so Bigram inherits the equals method from Object. This equals method tests for object identity, just like the == operator. Each of the ten copies of each bigram is distinct from the other nine, so they are deemed unequal by Object.equals, which explains why the program prints 260.

显然,Bigram 类的作者打算覆盖 equals 方法(Item-10),甚至还记得要一并覆盖 hashCode(Item-11)。不幸的是,我们的程序员没有覆盖 equals,而是重载了它(Item-52)。要覆盖 Object.equals,你必须定义一个 equals 方法,它的参数是 Object 类型的,但是 Bigram 的 equals 方法的参数不是 Object 类型的,所以 Bigram 从 Object 继承 equals 方法。这个继承来的 equals 方法只能检测对象同一性,就像 == 操作符一样。每 10 个 bigram 副本为一组,每组中的每个 bigram 副本都不同于其他 9 个,因此 Object.equals 认为它们不相等,这就解释了为什么程序最终打印 260。

Luckily, the compiler can help you find this error, but only if you help it by telling it that you intend to override Object.equals. To do this, annotate Bigram.equals with @Override, as shown here:

幸运的是,编译器可以帮助你找到这个错误,但前提是你告诉它你打算覆盖 Object.equals。为此,请使用 @Override 注解标记 Bigram.equals,如下所示:

@Override
public boolean equals(Bigram b) {
    return b.first == first && b.second == second;
}

If you insert this annotation and try to recompile the program, the compiler will generate an error message like this:

如果你插入此注解并尝试重新编译程序,编译器将生成如下错误消息:

Bigram.java:10: method does not override or implement a method from a supertype
@Override public boolean equals(Bigram b) {
^

You will immediately realize what you did wrong, slap yourself on the forehead, and replace the broken equals implementation with a correct one (Item 10):

你会立刻意识到自己做错了什么,拍拍自己的额头,用正确的方式替换不正确的 equals 实现(Item-10):

@Override
public boolean equals(Object o) {
    if (!(o instanceof Bigram))
        return false;
    Bigram b = (Bigram) o;
    return b.first == first && b.second == second;
}

Therefore, you should use the Override annotation on every method declaration that you believe to override a superclass declaration. There is one minor exception to this rule. If you are writing a class that is not labeled abstract and you believe that it overrides an abstract method in its superclass, you needn’t bother putting the Override annotation on that method. In a class that is not declared abstract, the compiler will emit an error message if you fail to override an abstract superclass method. However, you might wish to draw attention to all of the methods in your class that override superclass methods, in which case you should feel free to annotate these methods too. Most IDEs can be set to insert Override annotations automatically when you elect to override a method.

因此,你应该在 要覆盖超类声明的每个方法声明上使用 @Override 注解。 这条规则有一个小小的例外。如果你正在编写一个没有标记为 abstract 的类,并且你认为它覆盖了其超类中的抽象方法,那么你不必费心在这些方法上添加 @Override 注解。在未声明为抽象的类中,如果未能覆盖抽象超类方法,编译器将发出错误消息。但是,你可能希望让类中覆盖超类方法的所有方法更加引人注目,在这种情况下,你也可以自由选择是否注解这些方法。大多数 IDE 都可以设置为在选择覆盖方法时自动插入覆盖注解。

Most IDEs provide another reason to use the Override annotation consistently. If you enable the appropriate check, the IDE will generate a warning if you have a method that doesn’t have an Override annotation but does override a superclass method. If you use the Override annotation consistently, these warnings will alert you to unintentional overriding. They complement the compiler’s error messages, which alert you to unintentional failure to override. Between the IDE and the compiler, you can be sure that you’re overriding methods everywhere you want to and nowhere else.

大多数 IDE 都提供了一致使用 @Override 注解的另一个原因。如果启用适当的检查,如果你的方法没有 @Override 注解,但确实覆盖了超类方法,IDE 将生成警告。如果你一致地使用 @Override 注解,这些警告将提醒你防止意外覆盖。它们补充编译器的错误消息,这些错误消息会警告你无意的覆盖错误。在 IDE 和编译器的帮助下,你可以确保在任何你想要实施覆盖的地方都覆盖了,而没有遗漏。

The Override annotation may be used on method declarations that override declarations from interfaces as well as classes. With the advent of default methods, it is good practice to use Override on concrete implementations of interface methods to ensure that the signature is correct. If you know that an interface does not have default methods, you may choose to omit Override annotations on concrete implementations of interface methods to reduce clutter.

@Override 注解可用于覆盖接口和类声明的方法声明。随着默认方法的出现,最好对接口方法的具体实现使用 @Override 来确保签名是正确的。如果你知道接口没有默认方法,你可以选择忽略接口方法的具体实现上的 @Override 注解,以减少混乱。

In an abstract class or an interface, however, it is worth annotating all methods that you believe to override superclass or superinterface methods, whether concrete or abstract. For example, the Set interface adds no new methods to the Collection interface, so it should include Override annotations on all of its method declarations to ensure that it does not accidentally add any new methods to the Collection interface.

然而,在抽象类或接口中,标记覆盖超类或超接口方法的所有方法是值得的,无论是具体的还是抽象的。例如,Set 接口不会向 Collection 接口添加任何新方法,因此它的所有方法声明的应该包含 @Override 注解,以确保它不会意外地向 Collection 接口添加任何新方法。

In summary, the compiler can protect you from a great many errors if you use the Override annotation on every method declaration that you believe to override a supertype declaration, with one exception. In concrete classes, you need not annotate methods that you believe to override abstract method declarations (though it is not harmful to do so).

总之,如果你在每个方法声明上都使用 @Override 注解来覆盖超类型声明(只有一个例外),那么编译器可以帮助你减少受到有害错误的影响。在具体类中,可以不对覆盖抽象方法声明的方法使用该注解(即使这么做也并不会有害)。


Back to contents of the chapter(返回章节目录)

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,293评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,604评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,958评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,729评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,719评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,630评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,000评论 3 397
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,665评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,909评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,646评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,726评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,400评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,986评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,959评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,197评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,996评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,481评论 2 342

推荐阅读更多精彩内容

  • Chapter 6 Enums and Annotations 枚举和注解 JAVA supports two s...
    LaMole阅读 792评论 0 2
  • rljs by sennchi Timeline of History Part One The Cognitiv...
    sennchi阅读 7,283评论 0 10
  • 在非洲的戈壁滩上 有一种伊米的小花 能开出四彩的颜色 红的热情 黄的淡雅 蓝的梦幻 白的无暇 在这干旱恶劣的气候里...
    妖娆痞子阅读 589评论 0 0
  • 格拉宁在《奇特一生》中赞美柳比歇夫时说:“他通过他的方法证明,如果把一切集中到一个目标上,可以取得那么多的成就。只...
    216847ec520a阅读 303评论 0 3
  • 当你觉得从出生到阅读终末之诗的时间越来越短时,你应该尝试服务器。 所有的服务器都是PVP形式,在那里,没有无敌...
    Minecraft_PE阅读 150评论 0 0