`
Cwind
  • 浏览: 262794 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
博客专栏
793bb7df-a2a9-312d-8cb8-b66c3af482d1
LeetCode题解
浏览量:52415
社区版块
存档分类
最新评论

Object.equals方法:重载还是覆盖

阅读更多

本文译自StackOverflow上对此问题的讨论。

原问题链接

 

在阅读Joshua Bloch的《Effective Java(第二版)》第8条“覆盖equals时请遵守通用约定”时对如下论述有疑问:

“不要将equals声明中的Object对象替换为其他的类型。程序员编写出下面这样的equals方法并不鲜见,这会使程序员花上数个小时都搞不清它为什么不能正常工作:”

public boolean equals(MyClass o) {
    //...
}

 “问题在于,这个方法并没有覆盖(override)Object.equals,因为它的参数应当是Object类型,相反,它重载(overload)了Object.equals。”

 

问题:

为何代码示例中的强类型的equals方法重载并不足够?书中提到重载而非覆盖会引起问题,但并未论述为何如此也没有说明在何种场景下会使得equals方法失败。

 

回答:

这是因为重载此方法并不会改变集合类或者其他地方显式调用equals(Object)的行为。例如:

public class MyClass {

    public boolean equals(MyClass m) {
        return true;
    }
}

如果把它放到HashSet中:

public static void main(String[] args) {
    Set<MyClass> myClasses = new HashSet<>();
    myClasses.add(new MyClass());
    myClasses.add(new MyClass());
    System.out.println(myClasses.size());
}

上面程序将会打印出2,而不是1。虽然你期望所有的MyClass实例经由重载方法判断都是相等,并且集合不会添加第二个实例。

所以基本上,即使下面表达式为true:

MyClass myClass = new MyClass();
new MyClass().equals(myClass);

 下述表达式依然为false:

Object o = new MyClass();
new MyClass().equals(o);

后一个表达式是集合或其他类用于判断相等性的。事实上,只有当参数显式地为MyClass或其子类型的实例时,才会调用到重载方法并返回true。

 

关于覆盖还是重载的问题:

让我们从覆盖和重载的区别说起。通过覆盖,你事实上重新定义了这个方法。事实上相当于你删除了方法原始的实现并替换为自己的实现。所以当你这样做时:

@Override
public boolean equals(Object o) { ... }

你事实上重新链接了你的equals实现以取代Object类(或者实现该方法的最后一个父类)中的实现。

另一方面,当你这样做:

public boolean equals(MyClass m) { ... }

你定义了一个全新的方法,因为你定义了一个拥有同样名字但是不同参数列表的方法。当HashSet调用equals时,它调用的是参数类型为Object的方法。

Object k;
if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {

 (上述代码来自HashMap.put,被用作HashSet.add的底层实现。)

为了更清楚,MyClass中的equals方法只有当被覆盖时才会被调用,而不是被重载的时候。如果你试图在一个重载的equals方法上添加@Override注解,它将会产生一个编译错误,指出它并没有覆盖一个方法。我们可以在一个类中声明两个equals方法,因为这是重载:

public class MyClass {

    @Override
    public boolean equals(Object o) {
        return false;
    }

    public boolean equals(MyClass m) {
        return true;
    }
}

 

泛型

谈到泛型,equals方法并不是泛型。它显式地要求Object作为它的参数类型。当你试图这样做时:

public class MyGenericClass<T> {

    public boolean equals(T t) {
        return false;
    }
}

它将不会编译,错误信息:命名冲突,MyGenericClass的equals(T)方法类型擦除后与Object类中equals(Object)相同,但并未覆盖它

Name clash: The method equals(T) of type MyGenericClass has the same erasure as equals(Object) of type Object but does not override it

 当添加@Override注解时:

public class MyGenericClass<T> {

    @Override
    public boolean equals(T t) {
        return false;
    }
}

 错误信息变为:MyGenericClass的equals(T)方法必须覆盖或实现父类方法

The method equals(T) of type MyGenericClass must override or implement a supertype method

于是怎么做都会有问题。原因在于Java通过类型擦除实现泛型。当Java在编译阶段检查完所有的泛型类型,事实上的运行时对象都会被Object取代。无论何时你看到T类型,事实上的字节码都会包含Object。这就是为何反射不能用于泛型类型以及list instanceof List<String>将会出错的原因。

同样,这也使你无法重载泛型类型,如果有这样的类:

public class Example<T> {
    public void add(Object o) { ... }
    public void add(T t) { ... }
}

add(T)方法将会产生编译错误,因为类完成编译时,两个方法将会有同样的签名,public void add(Object)。

1
0
分享到:
评论

相关推荐

    如何在Java中避免equals方法的隐藏陷阱(上)

     本文描述重载equals方法的技术,这种技术即使是具现类的子类增加了字段也能保证equal语义的正确性。  在《Effective Java》的第8项中,Josh Bloch描述了当继承类作为面向对象语言中的等价关系的基础问题,要...

    net学习笔记及其他代码应用

    重载是方法的名称相同。参数或参数类型不同,进行多次重载以适应不同的需要 Override 是进行基类中函数的重写。为了适应需要。 6.如果在一个B/S结构的系统中需要传递变量值,但是又不能使用Session、Cookie、...

    Java入门1·2·3:一个老鸟的Java学习心得.PART3(共3个)

    7.5 方法重载(overload):给汽车加速添个限制 168 7.5.1 什么是方法的签名 168 7.5.2 什么是重载?为什么要重载? 168 7.5.3 给汽车加个重载的方法 169 7.5.4 测试一下 169 7.5.5 重载容易引发误解的两个地方...

    NET设计规范-.NET约定、惯用法与模式.part2

    8.7.1 Object.Equals 210 8.7.2 Object.GetHashCode 212 8.7.3 Object.ToString 213 8.8 Uri 214 8.9 System.Xml的使用 216 8.10 相等性操作符 218 8.10.1 值类型的相等性操作符 218 8.10.2 引用类型...

    NET设计规范-.NET约定、惯用法与模式.part1

    8.7.1 Object.Equals 210 8.7.2 Object.GetHashCode 212 8.7.3 Object.ToString 213 8.8 Uri 214 8.9 System.Xml的使用 216 8.10 相等性操作符 218 8.10.1 值类型的相等性操作符 218 8.10.2 引用类型...

    Java基础知识点总结.docx

    equals()方法和hashCode()方法 270 数据结构 273 Array方法类汇总 304 Java数组与集合小结 305 递归 309 对象的序列化 310 Java两种线程类:Thread和Runnable 315 Java锁小结 321 java.util.concurrent.locks包下...

    JAVA入门1.2.3:一个老鸟的JAVA学习心得 PART1(共3个)

    7.5 方法重载(overload):给汽车加速添个限制 168 7.5.1 什么是方法的签名 168 7.5.2 什么是重载?为什么要重载? 168 7.5.3 给汽车加个重载的方法 169 7.5.4 测试一下 169 7.5.5 重载容易引发误解的两个地方...

    整理后java开发全套达内学习笔记(含练习)

    Override [java] 方法的覆盖(覆盖父类的方法) [,әuvә'raid] polymiorphism[java] 多态 (polymorphism 多形性[,pɒli'mɒ:fizm]) allowing a single object to be seen as having many types. principle n.原则,...

    JAVA面试题最全集

    7.Java多态的实现(继承、重载、覆盖) 8.编码转换,怎样实现将GB2312编码的字符串转换为ISO-8859-1编码的字符串。 9.Java中访问数据库的步骤,Statement和PreparedStatement之间的区别。 10.找出下列代码可能...

    java常用工具类的使用

    就需要重载很多方法进行排序操作。而在Java类库中有一个Arrays类的sort方法已经实现各种数据类型的排序算法。程序员只需要调用该类的方法即可。 代码演示:Arrays实现排序 public static void main(String[] args) ...

    Java入门教程(微学苑)-part1

    4.5 继承中的方法的覆盖和重载 65 4.6 多态和动态绑定 66 4.7 动态绑定 69 4.8 instanceof 运算符 70 4.9 多态对象的类型转换 71 4.10 Java static关键字以及Java静态变量和静态方法 72 4.11 static 的内存分配 73 ...

    java初学者必看

    9.3.3 静态方法覆盖 9.3.4 覆盖与异常抛出 9.3.5 抽象方法覆盖 9.3.6 覆盖与重载 9.4 静态绑定与动态绑定 9.4.1 静态绑定 9.4.2 动态绑定 9.5 抽象类 9.5.1 抽象类概念 9.5.2 抽象类应用 9.5.3 抽象类注意...

    编程技能训练与等级考试辅导:继承、super关键字.pptx

    Object类的equals()方法 ArrayList类 关于列表的一些有用的方法 自定义栈类 Protected数据和方法 防止继承和重写;利用继承性由父类创建子类 使用super调用父类构造方法和方法 在子类中覆盖父类的方法 描述类型转换和...

    【05-面向对象(下)】

    •Object类提供的equals方法判断两个对象相等的标准与==完全相同。因此开发者通常需要重写equals方法。 类成员 •在java类里只能包含Field,方法,构造器,初始化块,内部类(接口、枚举)等5种成员。 用...

Global site tag (gtag.js) - Google Analytics