Java 与equals方法相关的代码

Java code related to equals method

我正在备考,发现了一道我看不懂的例题。

对于以下代码,找出输出是什么:

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,但我不明白为什么。谁能帮帮我?

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

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

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

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

更详细的解释:

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

类型)

t3.equals() 可以执行 ObjectequalsTest 的等式,因为 t3 的编译时类型是 Test,而 Test class 有两个 equals 方法(一个继承自 Object class,另一个定义在 Test class).

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

Test 中的 equals 方法采用 Test 的实例。

之前的所有尝试都是用Object的实例进行的,它采用了Object的继承方法class:

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) 因此它是程序中实际调用该打印语句的唯一行。这个问题是为了教你一些东西。

  • 所有 class 隐式扩展对象
  • Object.java 包含一个采用 Object
  • 类型的 equals 方法
  • 可以存在多个具有相同名称的方法,前提是它们具有不同的参数 - 这称为方法重载
  • 方法方法重载谁的签名在运行时与参数匹配是被调用的方法。

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

public boolean equals(Test testje)

无法输入,因为在 运行 时参数是 Object 类型,它是 Test 的 Superclass,而不是 subclass。所以它会查看 Test.java 的隐式类型 superclass 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 中的某处找不到具有完全相同签名的方法则失败。这对于了解您是否确实 打算 重写一个方法很有用,并且您想要绝对确定您确实正在重写该方法并且您没有不小心更改 super 中的方法class 或 subclass 但不是两者都引入了 运行 时间错误,其中错误的方法实现被调用导致不需要的行为。

有两件重要的事情你应该知道。

  • 覆盖的方法必须具有与其超类具有的完全相同的签名。 (在你的例子中这个条件不满足。)

  • 在Java中对于一个对象,我们有两种类型:compile type and runtime type。在下面的示例中,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