当前位置: 首页 > 面试题库 >

覆盖Object.equals VS重载它

宰父学
2023-03-14
问题内容

阅读:Joshua Bloch撰写的有效Java-第二版

第8项-在覆盖等于时遵守总合同:

程序员编写看起来像这样的equals方法,然后花很多时间迷惑它为什么不能正常工作的情况并不少见:

[代码示例在这里]

问题在于此方法不会覆盖Object.equals,后者的参数类型为Object,而是将其重载。

代码样例:

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

我的问题:

为什么像此代码示例中那样重载的强类型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);

后者是集合和其他类用于确定相等性的版本。实际上,此参数将返回的 唯一 地方true是该参数显式MyClass为其子类型的实例或其子类型之一。

编辑: 根据您的问题:

覆盖与重载

让我们从覆盖和重载之间的区别开始。通过覆盖,您实际上可以 重新定义 该方法。您删除它的原始实现,然后用您自己的替换。因此,当您这样做时:

@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。)

需要明确的是,它会使用不同的唯一时间equals是当一个equals方法被 覆盖
,不会过载。如果尝试添加@Override到重载的equals方法中,它将因编译器错误而失败,并抱怨它没有覆盖方法。我什至可以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)相同的擦除,但不会覆盖它

如果您尝试这样@Override做:

public class MyGenericClass<T> {

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

您将得到以下信息:

类型MyGenericClass的equals(T)方法必须重写或实现一个超类型方法

所以你赢不了。这里发生的是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)



 类似资料:
  • 问题内容: 假设我在课堂上说两种方法 和 那是什么感觉 重载还是重载? 问题答案: 重载是指两个或多个具有相同名称但参数不同的方法,就像您的示例一样。重载是从接口或抽象类实现一个方法的,因此超类中的方法具有实现,而子类中的方法具有不同的实现,它们仍然具有相同的方法名称和参数。

  • 问题内容: 嗨,我只是想确保我正确理解这些概念。Java中的重载意味着您​​可以拥有具有不同数量的参数或不同数据类型的构造函数或方法。即 这个方法怎么样?由于它返回不同的数据类型,是否仍会被视为重载? 第二个问题是:java中有什么重载?它与继承有关吗?让我们有以下内容: 所以现在让我说以下 如果我打电话 这将返回车辆类的价格20,000 如果我打电话 这将返回卡车级别的价格14,000 我的知识

  • 重载-同一类中具有不同签名的同一方法 如果我在父类中有一个重载方法,那么子类会重载或重写这个特定的方法吗?

  • 问题内容: 我的域中有FinanceRequests和CommisionTransactions。如果我有一个FinanceRequests列表,则每个FinanceRequest可能包含多个需要撤回的CommisionTransactions。不用担心这是怎么做到的。 下面的类(非常底层)使我感到模糊不清,因为它简洁,可以很好地重用现有代码。一个问题类型擦除。 它们在擦除后具有相同的签名,即:

  • 本文向大家介绍C++中的重载、覆盖、隐藏介绍,包括了C++中的重载、覆盖、隐藏介绍的使用技巧和注意事项,需要的朋友参考一下 前几天面试时被问及C++中的覆盖、隐藏,概念基本答不上来,只答了怎么用指针实现多态,也还有遗漏。最终不欢而散。回来后在网上查找学习了一番,做了这个总结。其中部分文字借用了别人的博客,望不要见怪。 •概念 一、重载(overload) 指函数名相同,但是它的参数表列个数或顺序,

  • 问题内容: 我创建了一个自定义样式表,该样式表将覆盖Wordpress模板的原始CSS。但是,在我的日历页面上,原始CSS具有声明的每个表格单元格的高度: 有什么办法可以覆盖这个吗? 问题答案: 覆盖!important修饰符 只需使用添加另一个CSS规则,并赋予选择器更高的特异性(向选择器添加其他标签,id或类) 在比现有选择器晚的位置添加具有相同选择器的CSS规则(平局中,最后一个定义的获胜者