Динамическая привязка Java и переопределение метода
вчера у меня было двухчасовое техническое телефонное интервью (которое я прошел, woohoo!), но я полностью запорол следующий вопрос относительно динамической привязки в Java. И это вдвойне загадочно, потому что я преподавал эту концепцию студентам, когда был ассистентом несколько лет назад, поэтому перспектива, что я дал им дезинформацию, немного беспокоит...
вот проблема, которую мне дали:
/* 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 (поэтому статическая привязка будет вызывать Object's equals()
метод), динамическая привязка должны позаботьтесь о вызове наиболее конкретной версии метода на основе экземпляра типа ссылки. Что я упускаю?
13 ответов
Java использует статическую привязку для перегруженных методов и динамическую привязку для переопределенных. В вашем примере метод equals перегружен (имеет другой тип param, чем Object.equals ()), поэтому вызываемый метод привязан к ссылка тип во время компиляции.
обсуждение здесь
тот факт, что это метод equals, на самом деле не имеет значения, кроме того, что это распространенная ошибка перегружать, а не переопределять его, что вы уже знаете, основываясь на своем ответе на проблему в интервью.
изменить: Хорошее описание здесь как хорошо. Этот пример показывает аналогичная проблема, связанная с типом параметра, но что.
Я считаю, что если привязка на самом деле динамическая, то в любом случае, когда вызывающий объект и параметр являются экземпляром теста, будет вызван переопределенный метод. Так Т3.равенство (o1) будет единственным случаем это не напечатают.
на equals
метод Test
не перекроет equals
метод java.lang.Object
. Посмотрите на тип параметра! The Test
класс перегрузки equals
с методом, который принимает Test
.
если equals
метод предназначен для переопределения, он должен использовать аннотацию @Override. Это приведет к ошибке компиляции, чтобы указать на эту распространенную ошибку.
интересно, что в коде Groovy (который может быть скомпилирован в файл класса) все вызовы, кроме одного, будут выполнять оператор print. (Тот, кто сравнивает тест с объектом, явно не будет вызывать тест.функция equals (Test).) Это потому, что groovy делает полностью динамическую типизацию. Это особенно интересно, потому что у него нет переменных, которые явно динамически типизированы. Я прочитал в нескольких местах, что это считается вредным, как программисты ожидайте, что groovy сделает вещь java.
Java не поддерживает совместное отклонение параметров, только в возвращаемых типах.
другими словами, в то время как ваш тип возврата в переопределяющем методе может быть подтипом того, что он был в переопределенном, это неверно для параметров.
Если ваш параметр для equals в 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)
ссылки:
- Execute mean resolver, метод которого предпочитает использовать
- потому что не может переопределить метод с модификатором static, private или final
- 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);
}
}
Я нашел интересную статью о динамической и статической привязки. Он поставляется с куском кода для моделирования динамического связывания. Это сделало мой код более читаемым.
см. Также этот так вопрос, тесно связанный:переопределение метода Java equals quirk
ответ на вопрос "почему?"это то, как определяется язык Java.
цитировать статья Википедии о ковариации и Контравариации:
реализована ковариация возвращаемого типа на языке программирования Java версия в J2SE 5.0. Типы параметров быть точно таким же (инвариантным) для переопределение метода, в противном случае способ перегружен параллелью вместо этого определение.
другой языки разные.
очень ясно,что здесь нет концепции переопределения. Это перегрузка метода.
the Object()
метод класса 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; у нас есть ссылка of объект на o1 и t1 на equals()
метод. Таким образом, во время компиляции,equals()
метод файл будет ограничен.
Object t1 = new Test();
.
Поэтому во время выполнения он вызывает
public boolean equals(Object other)
что это
переопределенный метод
теперь, для значений счета как 4 и 6, это снова просто, что t3 имеющего ссылка и инициализации теста вызывает equals()
метод с параметром в качестве ссылок на объекты и является
перегруженный метод
ОК!
опять же, чтобы лучше понять, какой метод вызовет компилятор, просто нажмите на метод, и Eclipse выделит методы, аналогичные типы, которые, по его мнению, будут вызывать во время компиляции. Если не получится вызывается во время компиляции, то эти методы являются примером метод переменная.