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

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判断题含答案.doc

    对于Date类,如果覆盖了`equals()`方法,那么确实会比较对象的值。所以,题目描述是正确的。 2. 类加载器:类加载器负责加载运行所需的类,它确实区分本地文件系统和网络导入的类,增强了安全性。这个描述也是正确...

    java内部学习笔记.docx

    9. 重载和重写:方法名相同但参数列表不同的为方法重载,子类覆盖父类方法为方法重写。 10. 继承:子类继承父类,获得父类的属性和方法。 11. static:静态修饰符,用于声明类级别的变量和方法。 12. final:常量...

    C#串口编程Serialport类1.doc

    6. Equals:确定两个Object实例是否相等。(从Object继承。) 7. GetHashCode:用作特定类型的哈希函数。(从Object继承。) 8. GetLifetimeService:检索控制此实例的生存期策略的当前生存期服务对象。(从...

    C#试题汇总(有答案) .pdf

    30. 重载规则:方法可以通过不同的参数列表重载,包括参数数量、类型和顺序,但不能仅凭返回类型不同来进行重载。 这些知识点涵盖了C#的基础语法、类与对象、数据类型、数组、接口、抽象类、循环、事件、类型转换等...

    java中级面试题整理

    4. "==”与equals方法:前者比较两个对象的引用是否相同,后者比较两个对象的内容是否相等。 5. Error与Exception:Error是程序无法恢复的严重错误,而Exception是程序可以捕获并处理的异常。 6. 初始化顺序:类静态...

    重写equals方法

    例如,public boolean equals(Object o) 是一个正确的重写方法,而 public boolean equals(String o) 是一个重载方法,而不是重写方法。 equals 方法的实现 ------------------------- equals 方法的实现需要遵循...

    Java基础面试专题1

    3. 方法重载:在同一类中,通过不同的参数列表实现多个同名方法。 Java 虚拟机(JVM)通过动态绑定技术实现多态,即在运行时判断对象的实际类型,调用相应的方法。 接口在Java中具有重要意义: 1. 规范:定义了一...

    java基础知识总结

    18. 方法与数组:方法是执行特定任务的代码块,数组是一组有序的数据集合。 19. 方法的重载:允许在同一个类中创建多个同名方法,但参数列表必须不同。 20. 可变参数:Java允许定义接受可变数量参数的方法。 21. ...

    java实习周记25篇.docx

    4. 重载(Overloading):方法名相同,但参数列表不同(参数类型或数量)。 5. 重写(Overriding):子类覆盖父类的方法,保持相同的签名和返回类型。 6. 抽象类和抽象方法:抽象类不能实例化,抽象方法只有声明,...

    东财《java》在线作业答案.doc

    包括访问修饰符(如public和protected)、面向对象概念(类、对象、继承、多态)、线程状态(就绪、运行、阻塞/休眠)、进制表示、`Object`类的特性、整型常量类型(int、long)、多态表现(方法覆盖、重载)、`...

    java 面试宝典

    2. equals()与hashCode():理解Object类中的默认实现,以及在自定义类中的重写规则。 3. ArrayList与LinkedList的源码分析:查找、插入和删除操作的源码实现。 4. HashMap的源码解析:哈希冲突的解决,扩容机制等。 ...

    优选JAVA复习PPT文档.ppt

    19. `equals()`方法:`equals("open")`用于比较字符串是否等于"open"。 20. 内部类:在类内部定义的类称为内部类,可以是静态或非静态的,用于封装特定的逻辑。 21. 数组操作:创建一个有5个元素的整数数组,找出...

    点net的面试题点net

    =`,同时修改`Equals`和`GetHashCode`方法以保持一致性。 9. 委托关键字:委托在C#中用于定义方法的引用,声明的关键字是`delegate`。 10. sealed类:使用`sealed`修饰的类不能被其他类继承。 11. 用户控件:在...

    JAVA基础考试题.docx

    7. 编译运行:未提供具体代码,但从题目描述看,如果类中有重载的equals方法,编译不会出错,运行时会根据调用的equals方法来决定输出结果,选项D正确。 8. 继承和接口:Java中类只能单继承,但可以多实现接口,...

    计算机应用大赛库计算机应用大赛库

    22. 比较枚举:比较枚举对象时,应使用`equals()`方法,因为枚举对象是单例的,`hashCode()`方法可能会产生相同的值,而直接使用`==`比较的是引用。 23. Java源代码:源文件扩展名为`.java`,一个源文件中可以有多...

    Java的一些分享

    11. ==与equals的区别:==比较的是两个变量的内存地址,而equals是Object类中的方法,比较的是内容。对于基本类型或String,equals会比较值。 12. 静态与实例变量:静态变量属于类,所有类的实例共享同一个静态变量...

    Java软件中级工程师笔试题-复杂逻辑.doc

    3. 方法重载:代码定义了两个同名方法`myMethod`,一个接受`String`参数,另一个接受`Object`参数。当传入`null`时,由于`null`可以被任何引用类型接受,因此会调用接受`Object`参数的方法,输出"object"。 4. 日期...

    C#程序设计-3期(KC008) KC008110100003-错误的认为==和Equals作用相同.docx

    首先,`Equals`方法是`System.Object`基类中定义的一个虚方法,可以被任何类重写以提供自定义的相等比较逻辑。默认情况下,`Equals`用于比较两个对象的内容是否相等,对于引用类型,如果两个对象引用的是堆上的同一...

    JavaOOP_第1章上机练习.zip

    8. 对象的比较:掌握equals()和hashCode()方法的使用,以及自定义比较逻辑。 通过这些基础练习,学习者将能够建立起对Java OOP的基本认识,并逐步提升编程能力。在实践中不断加深理解,有助于为后续更复杂的编程...

    java出学者必须掌握的

    15.`Object`类:所有Java类都默认继承自`Object`类,因此所有类都拥有`Object`类的成员方法,如`equals()`和`toString()`。 16.`equals()`与`toString()`方法:`equals()`用于比较两个对象是否相等,而`toString()`...

Global site tag (gtag.js) - Google Analytics