美文网首页
ITEM 40: 坚持使用 override 注解

ITEM 40: 坚持使用 override 注解

作者: rabbittttt | 来源:发表于2019-10-05 18:01 被阅读0次

    ITEM 40: CONSISTENTLY USE THE OVERRIDE ANNOTATION
      Java库包含几种注解类型。对于一个程序员来说,最重要的是@Override。此注解只能用于方法声明,它指示带注解的方法声明覆盖超类型中的声明。如果您始终使用这个注解,它将保护您免受大量恶意bug的攻击。考虑下面这个例子,其中类 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()); 
      }
    }
    

      主程序反复向一个集合添加26个bigram,每个bigram由两个相同的小写字母组成。然后它打印集合的大小。如果你试着运行这个程序,你会发现它输出的不是26而是260。有什么问题吗?
      显然,Bigram 类的作者打算重写 equals 方法(iem 10),甚至还记得同时重写 hashCode (item 11)。不幸的是,程序员没有重写equals,而是重载了它(item 52)。为了覆盖 Object 的 equals 方法,我们必须定义一个参数是Object类型的equals方法,但是 Bigram 的 equals 方法的参数不是 Object 类型的,所以 Bigram 继承了 Object 的 equals 方法。这个 equals 方法测试对象标识,就像 == 操作符一样。每一个双字母的十份副本都与其他九份不同,因此它们被认为是不相等的。等于,这就解释了为什么程序输出260。
      幸运的是,编译器可以帮助您找到这个错误,但前提是您要告诉它您打算覆盖 Object.equals。为此,请在 Bigram.equals 使用注解 @Override,如下所示:

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

      如果插入此注解并尝试重新编译程序,编译器将生成如下错误消息: "Bigram.java:10: method does not override or implement a method from a supertype"
      你会立刻意识到自己做错了什么,给自己一记耳光,用正确的一记替换掉错误的 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;
    }
    

      因此,您应该在您认为要覆盖超类声明的每个方法声明上使用 Override 注解。这条规则有一个小小的例外。如果您正在编写一个没有标记为abstract 的类,并且您认为它覆盖了其超类中的抽象方法,那么您不需要在该方法上添加 Override 注解。在没有声明抽象的类中,如果未能覆盖抽象超类方法,编译器将发出错误消息。
      不过,您可能希望将注意力放在类中覆盖超类方法的所有方法上,在这种情况下,您也可以随意注解这些方法。大多数 IDE 都可以设置为在选择重写方法时自动插入覆盖注解。
      大多数 IDE 都提供了一致使用覆盖注解的另一个原因:如果启用了适当的检查,如果您的方法没有覆盖注解,但是覆盖了超类方法,IDE 将生成一个警告。如果您始终如一地使用 Override 注解,这些警告将提醒您进行无意的覆盖。它们补充了编译器的错误消息,这些错误消息会警告您无意中覆盖失败。在 IDE 和编译器之间,您可以确保您要覆盖的方法精准的命中了,而不是在其他任何地方。
      覆盖注解可以用于覆盖来自接口和类的声明的方法声明。随着缺省方法的出现,在接口方法的具体实现上使用覆盖来确保签名是正确的,这是一个很好的实践。如果知道接口没有默认方法,可以选择忽略接口方法的具体实现上的覆盖注解,以减少混乱。
      但是,在抽象类或接口中,值得注解所有您认为覆盖超类或超接口方法的方法,无论是具体的还是抽象的。例如,Set 接口没有向 Collection 接口添加任何新方法,因此它应该包含覆盖其所有方法声明的注解,以确保不会意外地向集合接口添加任何新方法。
      总之,如果您在每个方法声明上都使用 Override 注解,那么编译器可以保护您不受很多错误的影响,因为您认为每个方法声明都要覆盖超类型声明,只有一个例外:在具体类中,您不需要注解您认为可以覆盖抽象方法声明的方法(尽管这样做没有害处)。

    相关文章

      网友评论

          本文标题:ITEM 40: 坚持使用 override 注解

          本文链接:https://www.haomeiwen.com/subject/zmtfuctx.html