在 Java 中重载 Equals 方法时的运行时行为
Behavior at Runtime when Overloading the Equals Method in Java
我有一个学生 class 扩展了一个人 class。我的 super class 中没有 equals 方法,只有 subclass 中的两个方法,如图所示。我试图了解我的第二个、第三个和第四个打印语句的运行时行为。语句 1 调用采用 student 参数的 equals 方法,这很有意义,因为要比较的两个对象都声明为 Student 类型。但是,语句 2 调用带有 person 参数的 equals 方法,而最后 2 个语句调用 Object class 中的 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
其中Person对象是调用Student的重载equals的参数。这就是您获得打印语句的原因。如果您希望将其作为 Student 进行比较,则需要对其进行类型转换。
在对 Person 对象调用 equals 的情况下,它最终 returning 一个 false,因为 .equals 没有在 Person 中实现 class ... 然后它默认为 Object 的实现,即“检查 Null,然后检查引用是否相同......否则 return false”。您可以在 JDK 的源代码中查看实现。
根据声明的类型,在编译时确定使用哪个重载方法。
在第二种情况下,您已将 s3 声明为一个 Person,因此使用了 equals(Person)
方法。
在第3和第4种情况下,您已将变量声明为Person。 Person 没有 equals() 方法,因此编译器假定您要使用 Object:
中的默认方法
boolean equals(Object o);
您没有在任何 类 中覆盖该方法,因此默认方法是在 运行 时使用的方法。
你犯的最大错误是,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
而不是 Person
或 Student
类型的定义参数.
关于您得到的输出:
在四个调用中的 none 中,将调用覆盖的 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
我有一个学生 class 扩展了一个人 class。我的 super class 中没有 equals 方法,只有 subclass 中的两个方法,如图所示。我试图了解我的第二个、第三个和第四个打印语句的运行时行为。语句 1 调用采用 student 参数的 equals 方法,这很有意义,因为要比较的两个对象都声明为 Student 类型。但是,语句 2 调用带有 person 参数的 equals 方法,而最后 2 个语句调用 Object class 中的 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
其中Person对象是调用Student的重载equals的参数。这就是您获得打印语句的原因。如果您希望将其作为 Student 进行比较,则需要对其进行类型转换。
在对 Person 对象调用 equals 的情况下,它最终 returning 一个 false,因为 .equals 没有在 Person 中实现 class ... 然后它默认为 Object 的实现,即“检查 Null,然后检查引用是否相同......否则 return false”。您可以在 JDK 的源代码中查看实现。
根据声明的类型,在编译时确定使用哪个重载方法。
在第二种情况下,您已将 s3 声明为一个 Person,因此使用了 equals(Person)
方法。
在第3和第4种情况下,您已将变量声明为Person。 Person 没有 equals() 方法,因此编译器假定您要使用 Object:
中的默认方法boolean equals(Object o);
您没有在任何 类 中覆盖该方法,因此默认方法是在 运行 时使用的方法。
你犯的最大错误是,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
而不是 Person
或 Student
类型的定义参数.
关于您得到的输出:
在四个调用中的 none 中,将调用覆盖的 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