与 equals 方法相关的 Java 代码

我正在为考试练习,发现了一个我不明白的样题。

对于下面的代码,查找输出内容:

public class Test {


private static int count = 0;


public boolean equals(Test testje) {
System.out.println("count = " + count);
return false;
}


public static void main(String [] args) {
Object t1 = new Test();
Object t2 = new Test();
Test t3 = new Test();
Object o1 = new Object();


++count; t1.equals(t2);
++count; t1.equals(t3);
++count; t3.equals(o1);
++count; t3.equals(t3);
++count; t3.equals(t2);
}
}

这段代码的输出是 count = 4,但我不明白为什么。有人能帮我吗?

3507 次浏览

您应该注意的第一件事是 public boolean equals(Test testje) 没有覆盖了 Objectequals,因为参数是 Test而不是 Object,所以签名不匹配。

因此,在执行 t3.equals(t3);时,main方法只调用 equals(Test testje)一次,因为这是唯一一种同时执行实例 equals的静态类型和参数类型的情况。

t3.equals(t3);是第4个 equals语句(在静态 count变量的4个增量之后) ,因此打印4。

所有其他 equals语句执行 Objectequals,因此不打印任何内容。

更详细的解释是:

无论参数的类型如何,t1.equals()都会调用 Objectequals,因为 t1的静态(编译时)类型是 Object,而 Test类不会重写该方法。Object类没有具有单个 Test参数的 equals方法,因此无论 t1的动态(运行时类型)如何,都不能调用 equals(Test testje)

t3.equals()可以执行 ObjectequalsTest的 equals,因为 t3的编译时类型是 Test,并且 Test类有两个 equals方法(一个从 Object类继承,另一个在 Test类中定义)。

选择的方法取决于参数的编译时类型: 1. 当参数为 Object(如 t3.equals(o1);t3.equals(t2);)时,调用 Objectequals并且不打印任何内容。 2.当参数是 Test时,就像在 t3.equals(t3);中一样,equals的两个版本都匹配该参数,但是由于方法重载的规则,选择具有最具体参数的方法 equals(Test testje),并打印 count变量。

Test 中的 equals 方法接受 Test 的一个实例。

以前的所有尝试都是使用 Object 的一个实例进行的,该实例从 Object 类获取继承的方法:

public boolean equals(Object o){
return this == o;
}

因为这里没有打印,所以它不会打印任何值。

您的 ++count;将增加 count 的值,因此当您实际调用

public boolean equals(Test testje){...

方法,如果该方法确实打印了该值,则 count 的值为4。

t3.equals(t3)是唯一具有与方法签名 public boolean equals (Test testje)匹配的正确参数的行,因此它是程序中实际调用 print 语句的唯一一行。这个问题是为了教你一些东西。

  • 所有类都隐式扩展 Object
  • Java 包含一个采用 Object 类型的 equals 方法
  • 可以存在多个同名的方法,只要它们具有不同的参数——这就是所谓的方法重载
  • 在运行时与参数匹配的方法方法重载就是被调用的方法。

本质上,这里的技巧是 Test 像所有 java 类一样隐式地扩展 Object。对象包含一个采用 Object 类型的 equals 方法。T1和 t2的类型使得在运行时参数永远不会与 Test 中定义的 equals 的方法签名匹配。相反,它总是调用 Object.java 中的 equals 方法,因为基类型是 Object,在这种情况下,您可以访问的唯一方法是 Object.java 中定义的方法,或者派生类型是 Object,在这种情况下

public boolean equals(Test testje)

无法输入,因为在这种情况下,在运行时参数是 Object 类型,它是 Test 的超类,而不是子类。因此,它将查看 Test.java 的隐式类型超类 Object.java 中的 equals 方法,该类也包含一个 equals 方法,该方法的方法签名恰好是

public boolean equals (Object o)

在这种情况下,它与我们在运行时的参数相匹配,所以这个 equals 方法就是执行的方法。

注意,在 t3.equals(t3)的情况下,t3的基类型和派生类型都是 Test。

Test t3 = new Test ();

这意味着在运行时,您正在调用 Test.java 中的 equals 方法,并且您传入的参数实际上是 Test 类型,因此方法签名匹配,并执行 Test.java 中的代码。此时 count == 4

额外的知识给你:

@Override

如果编译器没有在 Superclass 的某个地方找到具有完全相同签名的方法,你可能在一些地方看到过注释,它会明确地指示编译器失败。这是有用的,如果你确定 意图覆盖一个方法,你想要绝对确定你真的覆盖了方法,你没有意外地改变方法在父类或子类,但不是两者都引入了一个运行时错误,在错误的方法实现被调用导致不必要的行为。

有两件关键的事你应该知道。

  • 重写的方法必须具有与其超类所具有的完全相同的签名(在您的示例中,这个条件不符合)

  • 在 Java 中,对于一个对象,我们有两种类型: < em > 编译 类型和 < em > 运行时 类型。在下面的示例中,myobj的编译类型是 Object,但它的运行时类型是 Car

    public class Car{
    @Override
    public boolean equals(Object o){
    System.out.println("something");
    return false;
    }
    }
    

    Object myobj = new Car();

    您还应该注意到,myobj.equals(...)导致在控制台中打印 something