Динамическая привязка Java и переопределение метода


вчера у меня было двухчасовое техническое телефонное интервью (которое я прошел, woohoo!), но я полностью заглушил следующий вопрос относительно динамической привязки в Java. И это вдвойне озадачивает, потому что я привык преподавать эту концепцию студентам, когда я был TA несколько лет назад, поэтому перспектива того, что я дал им дезинформацию, немного беспокоит...

вот проблема, которую мне дали:

/* What is the output of the following program? */

public class Test {

  public boolean equals( Test other ) {
    System.out.println( "Inside of Test.equals" );
    return false;
  }

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

    int count = 0;
    System.out.println( count++ );// prints 0
    t1.equals( t2 ) ;
    System.out.println( count++ );// prints 1
    t1.equals( t3 );
    System.out.println( count++ );// prints 2
    t3.equals( o1 );
    System.out.println( count++ );// prints 3
    t3.equals(t3);
    System.out.println( count++ );// prints 4
    t3.equals(t2);
  }
}

я утверждал, что выход должен был быть два отдельных печать инструкций из переопределенного equals() метод: at t1.equals(t3) и t3.equals(t3). Последний случай достаточно очевиден, и с первым случаем, хотя t1 имеет ссылку на объект типа, он создается как тест типа, поэтому динамическая привязка должна вызывать переопределенную форму метода.

видимо, нет. Мой интервьюер призвал меня запустить программу самостоятельно, и вот, был только один выход из переопределенного метода: на линии t3.equals(t3).

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

13 87

13 ответов:

Java использует статическую привязку для перегруженных методов и динамическую привязку для переопределенных. В вашем примере метод equals перегружен (имеет другой тип param, чем Object.равно ()), поэтому вызываемый метод привязан к ссылка тип во время компиляции.

обсуждение здесь

тот факт, что это метод equals, на самом деле не имеет значения, кроме того, что это распространенная ошибка для перегрузки вместо переопределения, которая вы уже знаете, основываясь на вашем ответе на проблему в интервью.

изменить: Хорошее описание здесь как хорошо. Этот пример показывает аналогичная проблема, связанная с типом параметра, но что.

Я считаю, что если привязка была фактически динамической, то любой случай, когда вызывающий объект и параметр были экземпляром теста, приведет к вызову переопределенного метода. Так что Т3.равные (o1) были бы единственным случаем что бы не печатать.

The equals метод Test не перекроет equals метод java.lang.Object. Посмотрите на тип параметра! Элемент Test класс перегрузки equals с помощью метода, который принимает Test.

если equals метод предназначен для переопределения, он должен использовать аннотацию @Override. Это приведет к ошибке компиляции, чтобы указать на эту распространенную ошибку.

интересно, что в коде Groovy (который может быть скомпилирован в файл класса) все вызовы, кроме одного, выполняли бы оператор print. (Тот, кто сравнивает тест с объектом, явно не будет вызывать тест.равная (тестовая) функция.) Это потому, что groovy делает полностью динамическую типизацию. Это особенно интересно, потому что он не имеет каких-либо переменных, которые явно динамически типизированы. Я читал в нескольких местах, что это считается вредным, как программисты ожидайте, что groovy сделает java-вещь.

Java не поддерживает совместное отклонение параметров, только в возвращаемых типах.

Если ваш параметр для equals in Object является Object, размещение equals с чем-либо еще в подклассе будет перегруженным, а не переопределенным методом. Следовательно, единственная ситуация, когда этот метод будет вызван, когда статический тип параметр является тестовым, как и в случае T3.

удачи в процессе собеседования! Я хотел бы пройти собеседование в компании, которая задает такие вопросы вместо обычных вопросов algo/data structures, которые я преподаю своим студентам.

Я думаю, что ключ заключается в том, что метод equals() не соответствует стандарту: он принимает другой тестовый объект, а не объект объекта и, следовательно, не переопределяет метод equals (). Это означает, что вы на самом деле только перегрузили его, чтобы сделать что-то особенное, когда он задан тестовый объект, давая ему объект Object вызывает объект.равняется(объект o). Просмотр этого кода через любую IDE должен показать вам два метода equals () для тестирования.

метод перегружен вместо переопределения. Равные всегда принимают объект в качестве параметра.

кстати, у вас есть пункт об этом в эффективной java Блоха (что вы должны владеть).

какая-то заметка в Динамическое Связывание (DD) и Статическое Связывание(SB) после поиска некоторое время:

1.Сроки выполнения: (Ref.1)

  • DB: во время выполнения
  • SB: время компилятора

2.Используется для:

  • DB: переопределение
  • SB: перегрузка (статическая, частная, окончательная) (Ссылка.2)

ссылки:

  1. Execute mean resolver, какой метод предпочитают использовать
  2. потому что не может переопределить метод с модификатором static, private или final
  3. http://javarevisited.blogspot.com/2012/03/what-is-static-and-dynamic-binding-in.html

если добавлен другой метод, который переопределяет вместо перегрузки, он объяснит динамический вызов привязки во время выполнения.

/* каков результат работы следующей программы? * /

public class DynamicBinding {
    public boolean equals(Test other) {
        System.out.println("Inside of Test.equals");
        return false;
    }

    @Override
    public boolean equals(Object other) {
        System.out.println("Inside @override: this is dynamic binding");
        return false;
    }

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

        int count = 0;
        System.out.println(count++);// prints 0
        t1.equals(t2);
        System.out.println(count++);// prints 1
        t1.equals(t3);
        System.out.println(count++);// prints 2
        t3.equals(o1);
        System.out.println(count++);// prints 3
        t3.equals(t3);
        System.out.println(count++);// prints 4
        t3.equals(t2);
    }
}

Я нашел интересную статью о динамической и статической привязки. Он поставляется с куском кода для моделирования динамического связывания. Это сделало мой код более читаемым.

https://sites.google.com/site/jeffhartkopf/covariance

см. Также этот так вопрос, тесно связанный:переопределение метода JAVA equals quirk

ответ на вопрос "почему?"это то, как определяется язык Java.

цитировать статья в Википедии о ковариации и Контравариации:

ковариация возвращаемого типа реализована на языке программирования Java версия J2SE 5.0. Типы параметров быть точно таким же (инвариантным) для метод переопределения, в противном случае метод перегружается с параллельным вместо этого определение.

другой языки бывают разные.

очень ясно, что здесь нет понятия переопределения. Это перегрузка метода. элемент Object() метод класса объекта принимает параметр ссылки типа Object и this equal() метод принимает параметр ссылки типа Test.

я попытаюсь объяснить это с помощью двух примеров, которые являются расширенными версиями некоторых примеров, с которыми я столкнулся в интернете.

public class Test {

    public boolean equals(Test other) {
        System.out.println("Inside of Test.equals");
        return false;
    }

    @Override
    public boolean equals(Object other) {
        System.out.println("Inside of Test.equals ot type Object");
        return false;
    }

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

        int count = 0;
        System.out.println(count++); // prints 0
        o1.equals(t2);

        System.out.println("\n" + count++); // prints 1
        o1.equals(t3);

        System.out.println("\n" + count++);// prints 2
        t1.equals(t2);

        System.out.println("\n" + count++);// prints 3
        t1.equals(t3);

        System.out.println("\n" + count++);// prints 4
        t3.equals(o1);

        System.out.println("\n" + count++);// prints 5
        t3.equals(t3);

        System.out.println("\n" + count++);// prints 6
        t3.equals(t2);
    }
}

здесь, для строк со значениями счета 0, 1, 2 и 3; у нас есть ссылка на объект на o1 и t1 на equals() метод. Таким образом, во время компиляции,equals() метод , даже хотя ссылка на t1 и объект и intialization на тест класс.
Object t1 = new Test();.
Поэтому во время выполнения он вызывает public boolean equals(Object other) что это

переопределенный метод

. enter image description here

теперь, для подсчета значений как 4 и 6, это снова просто, что t3 имеющего ссылка и инициализации из теста звонит equals() метод с параметром в качестве ссылки на объект и

перегруженный метод

ОК!

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

enter image description here