Вчера у меня было двухчасовое собеседование по телефону по техническим вопросам (которое я прошел, у-у-у!), Но я полностью не ответил на следующий вопрос, касающийся динамического связывания в 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
имеется ссылка на тип Object, он создается как тип Test, поэтому динамическое связывание должно вызывать переопределенную форму метода.
Очевидно нет. Мой интервьюер посоветовал мне запустить программу самостоятельно, и, о чудо, был только один результат переопределенного метода: в строке t3.equals(t3)
.
Тогда мой вопрос: почему? Как я уже упоминал, даже если t1
это ссылка типа Object (поэтому статическая привязка будет вызывать equals()
метод Object ), динамическая привязка должна позаботиться о вызове наиболее конкретной версии метода на основе созданного типа ссылки. Что мне не хватает?
источник
Ответы:
Java использует статическую привязку для перегруженных методов и динамическую привязку для переопределенных. В вашем примере метод equals перегружен (имеет другой тип параметра, чем Object.equals ()), поэтому вызываемый метод привязан к ссылочному типу во время компиляции.
Некоторое обсуждение здесь
Тот факт, что это метод equals, на самом деле не актуален, за исключением того, что это распространенная ошибка - его перегрузка, а не переопределение, о чем вы уже знаете, основываясь на своем ответе на проблему в интервью.
Изменить: здесь также хорошее описание . В этом примере показана аналогичная проблема, связанная с типом параметра, но вызванная той же проблемой.
Я считаю, что если бы привязка была действительно динамической, то в любом случае, когда вызывающий объект и параметр были экземпляром Test, был бы вызван переопределенный метод. Таким образом, t3.equals (o1) будет единственным случаем, который не будет печататься.
источник
equals
МетодTest
не переопределяетequals
методjava.lang.Object
. Посмотрите на тип параметра!Test
Класс перегрузкиequals
с помощью метода , который принимаетTest
.Если
equals
метод предназначен для переопределения, он должен использовать аннотацию @Override. Это может привести к ошибке компиляции, чтобы указать на эту распространенную ошибку.источник
Интересно, что в коде Groovy (который может быть скомпилирован в файл класса) все вызовы, кроме одного, будут выполнять оператор печати. (Тот, который сравнивает Test с Object, явно не вызовет функцию Test.equals (Test).) Это потому, что groovy ДЕЙСТВИТЕЛЬНО выполняет полностью динамическую типизацию. Это особенно интересно, потому что в нем нет переменных, которые явно динамически типизируются. Я читал в паре мест, что это считается вредным, поскольку программисты ожидают, что groovy сможет делать Java-вещь.
источник
Java не поддерживает ковариацию параметров, только возвращаемых типов.
Другими словами, хотя ваш тип возвращаемого значения в методе переопределения может быть подтипом того, что было в переопределенном методе, это неверно для параметров.
Если вашим параметром для equals в Object является Object, установка равенства с чем-либо еще в подклассе будет перегруженным, а не переопределенным методом. Следовательно, единственная ситуация, в которой будет вызван этот метод, - это когда статическим типом параметра является Test, как в случае T3.
Удачи в прохождении собеседования! Я бы хотел пройти собеседование в компании, которая задает такие вопросы вместо обычных вопросов об алгоритмах / структурах данных, которым я обучаю своих студентов.
источник
Я думаю, что ключ кроется в том, что метод equals () не соответствует стандарту: он принимает другой объект Test, а не объект Object, и, следовательно, не отменяет метод equals (). Это означает, что вы фактически перегрузили его только для того, чтобы сделать что-то особенное, когда ему дали объект Test, а объект объекта вызывает Object.equals (Object o). Просмотр этого кода в любой среде IDE должен показать вам два метода equals () для Test.
источник
Метод перегружен, а не переопределен. Equals всегда принимает объект как параметр.
кстати, у вас есть пункт об этом в эффективной java Блоха (который вам должен принадлежать).
источник
Некоторые заметки в Dynamic Binding (DD) и Static Binding̣̣̣ (SB) после некоторого поиска:
1.Время выполнения : ( Ссылка 1)
2. используется для :
Справка:
источник
Если добавлен другой метод, который переопределяет вместо перегрузки, он объяснит вызов динамической привязки во время выполнения.
/ * Что выдает следующая программа? * /
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.
Процитируем статью в Википедии о ковариации и контравариантности :
Остальные языки разные.
источник
Совершенно ясно, что здесь нет концепции переопределения. Это перегрузка метода.
Object()
метод класса Object принимает параметр ссылки типа Object и этот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()
метод из файла Object.class будет ограничен.Однако, несмотря на то, ссылка на t1 является объектом , он имеет intialization из класса Test .
Object t1 = new Test();
.Следовательно, во время выполнения он вызывает то,
public boolean equals(Object other)
что является.
Теперь для значений count как 4 и 6 снова ясно, что t3 который имеет ссылку и инициализацию Test, вызывает
equals()
метод с параметром как ссылки на объекты и являетсяХОРОШО!
источник