Java-код, связанный с методом equals

Я практикуюсь на экзамене и нашел образец проблемы, который я не понимаю.

Для следующего кода найдите результат:

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, но я не понимаю, почему. Кто-нибудь может мне помочь?

Ответ 1

В первую очередь следует отметить, что public boolean equals(Test testje) не переопределяет Object equals, так как аргумент Test вместо Object, поэтому сигнатуры на ' t.

Поэтому метод main вызывает equals(Test testje) ровно один раз - при выполнении t3.equals(t3); - поскольку это единственный случай, в котором выполняется как статический тип экземпляра equals, так и тип аргумента Test.

t3.equals(t3); - это 4-й оператор equals (который приходит после 4 шагов статической переменной count), поэтому 4. печатается 4.

Все остальные операторы equals выполняют Object equals и поэтому ничего не печатают.

Более подробное объяснение:

t1.equals() вызывает Object equals независимо от типа аргумента, поскольку тип статического (время компиляции) t1 равен Object, а класс Test не переопределяет этот метод, Класс Object не имеет метода equals с единственным аргументом Test, поэтому equals(Test testje) не может быть вызван независимо от динамического (тип выполнения) t1.

t3.equals() может выполнять либо Object equals, либо Test равно, поскольку тип времени компиляции t3 равен Test, а класс Test имеет два метода equals (один унаследованный из класса Object и другого, определенного в классе Test).

Выбранный метод зависит от типа времени компиляции аргумента: 1. Когда аргумент Object (как в t3.equals(o1); или t3.equals(t2);), вызывается Object equals и ничего не печатается. 2. Когда аргумент Test, как в t3.equals(t3);, обе версии equals соответствуют этому аргументу, но из-за правил перегрузки метода выбирается метод с наиболее конкретным аргументом - equals(Test testje) - и изменена переменная count.

Ответ 2

Метод equals в Test принимает экземпляр Test.

Все предыдущие попытки были сделаны с экземпляром Object, который берет унаследованный метод из класса Object:

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

Поскольку там нет печати, он не будет печатать никаких значений.

Ваш ++count; будет увеличивать значение count, поэтому момент, когда вы действительно вызываете

public boolean equals(Test testje){...

который печатает это значение, значение count равно 4.

Ответ 3

t3.equals(t3) - единственная строка, которая имеет правильные аргументы, соответствующие сигнатуре метода public boolean equals (Test testje), поэтому это единственная строка в программе, которая фактически вызывает этот оператор печати. Этот вопрос призван научить вас нескольким вещам.

  • Все классы неявно расширяют Object
  • Object.java содержит метод equals, который принимает тип Object
  • несколько методов с тем же именем могут существовать, если они имеют разные аргументы - это называется перегрузкой метода
  • метод метода overload, подпись которого соответствует аргументам во время выполнения, - это метод, который вызывается.

По сути, здесь есть трюк, что Test неявно расширяет Object, как и все классы java. Объект содержит метод equals, который принимает тип Object. t1 и t2 набираются так, что во время выполнения аргументы никогда не соответствуют сигнатуре метода равным, которая определена в тесте. Вместо этого он всегда вызывает метод equals в Object.java, потому что либо базовый тип Is Object, и в этом случае единственными методами, к которым у вас есть доступ, являются те, которые определены в Object.java, или производный тип - Object, в этом случае

public boolean equals(Test testje)

Невозможно ввести, потому что в этом случае во время выполнения аргумент имеет тип Object, который является суперклассом теста, а не подклассом. Таким образом, вместо этого он смотрит на метод equals в Test.java неявно типизированном суперклассе Object.java, который также содержит метод equals, который просто имеет подпись метода

public boolean equals (Object o)

которые в этом случае соответствуют нашим аргументам во время выполнения, поэтому этот метод равен методу, который выполняется.

Обратите внимание, что в случае t3.equals(t3) и базовый тип, и производный тип t3 - это Test.

Test t3 = new Test ();

это означает, что во время выполнения вы вызываете метод equals в Test.java, и аргумент, который вы передаете, фактически имеет тип Test, поэтому соответствие сигнатур метода и код внутри Test.java выполняется. В этот момент count == 4.

Бонусный бит знаний для вас:

@Override 
Аннотации

которые вы, возможно, видели в нескольких местах, явно инструктируют компилятор сбой, если он не находит метод с той же самой подписью где-то в суперклассе. Это полезно знать, если вы определенно намереваетесь переопределить метод и хотите быть абсолютно уверенным, что вы действительно переопределяете метод, и вы случайно не изменили метод в суперклассе или подклассе, но не оба, и введена ошибка времени выполнения, когда вызывается неправильная реализация метода, вызывающая нежелательное поведение.

Ответ 4

Есть две ключевые вещи, которые вы должны знать.

  • Переопределенные методы должны иметь точные подписи, так как их суперкласс. (в вашем примере это условие не соответствует.)

  • В Java для объекта мы имеем два типа: тип компиляции и тип выполнения. В следующем примере тип компиляции 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 в консоли.