【问题标题】:Behavior at Runtime when Overloading the Equals Method in Java [duplicate]在 Java 中重载 Equals 方法时的运行时行为 [重复]
【发布时间】:2020-12-20 18:11:02
【问题描述】:

我有一个扩展 Person 类的 Student 类。我的超类中没有 equals 方法,只有我的子类中的两个,如图所示。我试图了解我的第二个、第三个和第四个打印语句的运行时行为。语句 1 调用了接受 student 参数的 equals 方法,这很有意义,因为被比较的两个对象都声明为 Student 类型。但是,语句 2 调用了接受 person 参数的 equals 方法,而最后 2 个语句调用了 Object 类中的 equals 方法。有人可以解释为什么当 Java 是动态类型的并且实际运行时对象始终是 Student 时会出现这种情况。如有任何错误,请提前道歉!我是新来的,也是 Java 新手。我不太关心每个方法的输出,只是调用了哪个方法以及为什么。

public boolean equals(Student s) {
    System.out.println("inside student equals");
    return true;
}

public boolean equals(Person p) {
    System.out.println("inside person equals");
    return false;
}

public static void main(String[] args) {
    Student s1 = new Student("John", "1", 10, 1.0, 10);
    Student s2 = new Student("John", "1", 10, 1.0, 10);

    Person s3 = new Student("John", "1", 10, 1.0, 10);
    Person s4 = new Student("John", "1", 10, 1.0, 10);

    System.out.println(s1.equals(s2)); // 1
    System.out.println(s1.equals(s3)); // 2

    System.out.println(s3.equals(s4)); // 3
    System.out.println(s3.equals(s1)); // 4
}

输出:

    inside student equals
    true
    inside person equals
    false
    false
    false

【问题讨论】:

  • 请发送文本,而不是文本图像。当它是图像时,很难测试您的代码并对其进行编辑。
  • 我不知道怎么做? :\
  • @Steve 我现在做
  • @Progman 确实如此,部分!我仍在尝试找出为什么在语句 3 和 4 中调用对象类中的等号而不是学生类中的两个中的一个

标签: java overriding overloading


【解决方案1】:

您犯的最大错误是,equals 方法缺少 @Override 注释。一旦你会这样做,例如

@Override
public boolean equals(Object obj) {
    System.out.println("inside person equals");
    return false;
}

@Override
public boolean equals(Object obj) {
    System.out.println("inside student equals");
    return true;
}

一旦你这样做了,你的 IDE 将导致编译失败,迫使你更正定义应该是 Object 而不是 PersonStudent 类型作为参数。

关于你得到的输出:

在这四个调用中,都不会调用被覆盖的Object#equals

毫无疑问,Student#equals 将在第一次调用中被调用,因为两个引用都是Student 类型。在剩下的三个调用中,Person#equals 将因为最接近的匹配而被调用。

您可以查看this demo 获取插图。

更新

如果您将两个equals 方法都放在Student 中,如图here 所示,那么您应该更容易理解输出。

s1.equals(s2) // 1 -> "inside student equals" will be printed because the param, s2 is of type, Student
s1.equals(s3) // 2 -> "inside person equals" will be printed because the param, s3 is of type, Person

s3.equals(s4) // 3 -> s3 is of type, Person but Person has not overridden equals, therefore Object#equals will be called
s3.equals(s1) // 4 -> s3 is of type, Person but Person has not overridden equals, therefore Object#equals will be called

如果您希望在最后两种情况下也调用Student#equals,则需要将s3 转换为Student,如下所示:

System.out.println(((Student) s3).equals(s4)); // 3.1
System.out.println(((Student) s3).equals(s1)); // 4.1

【讨论】:

  • 您好,Arvind,感谢您的回复。我无法理解的是,如果永远不会调用 Object equals 并且只是像您所说的那样等于 Person,那么为什么它不在最后两个语句中打印“inside person equals”?
  • @amuu00 - 我添加了演示链接。如有任何疑问/问题,请随时发表评论。
  • 这很有意义,但这并不是我的问题。我在子类中有equals(Student s)和equals(Person p),而在我的超类中没有。你能帮忙解释打印语句 3 和 4 吗?我得到了前两个
  • @amuu00 - 我添加了一个更新部分来解释此要求的输出。如有任何疑问/问题,请随时发表评论。
  • 完成!感谢 Arvind,我的困惑来自以下几点: s3.equals(s4) //s3 在声明时属于 Person 类型,并且代码可以编译,因为它从对象类继承了 equals 方法并且编译器很高兴。然而,在运行时,由于 Java 是动态类型的并且我们有 Person s3 = new Student(),它会在 Student 类中运行 equals。你看?我在想它应该在 Student 类中运行两个 equals 方法之一。
【解决方案2】:

其中 Person 对象是调用 Student 的重载 equals 的参数。这就是你得到 print 语句的原因。如果您希望将其作为学生进行比较,则需要对其进行类型转换。

在 Person 对象上调用 equals 的情况下,它最终会返回 false,因为 .equals 没有在 Person 类中实现……然后它默认为 Object 的实现,即“检查 Null,然后检查引用是否为相同...否则返回false”。您可以在 JDK 的源代码中查看实现。

【讨论】:

    【解决方案3】:

    确定使用哪个重载方法,是在编译时根据声明的类型确定的。

    在第二种情况下,您已将 s3 声明为 Person,因此使用了 equals(Person) 方法。

    在第 3 和第 4 种情况下,您已将变量声明为 Person。 Person 没有 equals() 方法,因此编译器假定您要使用 Object 的默认方法:

    boolean equals(Object o);
    

    您没有在任何类中重写该方法,因此默认方法是运行时使用的方法。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-02-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多