Статическое и динамическое связывание Java, upcast, перегрузка, смешанная вместе
Допустим, у нас есть следующий код
class TestEqual{
public boolean equals(TestEqual other ) {
System.out.println( "In equals from TestEqual" ); return false;
}
public static void main( String [] args ) {
Object t1 = new TestEqual(), t2 = new TestEqual();
TestEqual t3 = new TestEqual();
Object o1 = new Object();
int count = 0;
System.out.println( count++ );// shows 0
t1.equals( t2 ) ;
System.out.println( count++ );// shows 1
t1.equals( t3 );
System.out.println( count++ );// shows 2
t3.equals( o1 );
System.out.println( count++ );// shows 3
t3.equals(t3);
System.out.println( count++ );// shows 4
t3.equals(t2);
}
}
По сути, в классе TestEqual (который, конечно, расширяет Object) у нас есть метод, который перегружает метод equals из Object.
Кроме того, у нас есть некоторые переменные: Object t1, t2 экземпляры как TestEqual, TestEqual t3 экземпляры как TestEqual и Object o1 экземпляры как Объект.
Если мы запустим программу, это будет вывод.
0
1
2
3
In equals from TestEqual
4
Этот пример выглядит немного сложнее, чем обычный Car c = new Vehicle(); c.drive(); потому что экземпляр объекта, из которого мы вызываем метод, отличается от его типа, а также параметр метода отличается от его типа.
Я хотел бы проверить, правильно ли я понял, что происходит, когда мы вызываем каждый метод, шаг за шагом в отношении связывания.
show 0
t1.equals(t2)
show 1
t1 считается объектом TestEqual. Метод equals перегружен, поэтому привязка является статической, это означает, что мы передадим t2 как Object, поэтому он вызовет метод equals, унаследованный от суперкласса Object, поэтому он не будет показывать никакого текста.
show 1
t1.equals(t3)
show 2
Это кажется немного странным. Я ожидал бы показать "In equals from TestEqual", потому что t3 является объектом TestEqual, поэтому должны вызываться equals from t1. Мое объяснение здесь состоит в том, что t1 является статической границей и рассматривается как объект, поэтому вызывается метод, унаследованный от класса Object, при этом параметр TestEqual t3 передается в объект. Но не значит ли это, что предыдущее объяснение из t1.equals(t2) неверно?
show 2
t3.equals(o1);
show 3
t3 - это объект TestEqual, параметр o1 - это объект, поэтому вызывается метод equals, унаследованный от Object, поэтому ничего не печатается.
show 3
t3.equals(t3)
show 4
t3 - объект TestEqual, параметр - объект TestEqual, поэтому будет вызван перегруженный метод из TestEqual, который выдает "In equals from TestEqual".
show 4
t3.equals(t2)
t3 - это объект TestEqual, параметр - это объект из-за статической привязки (перегруженный метод), поэтому вызывается равный метод, унаследованный от объекта, ничего не печатая.
2 ответа
Это кажется немного странным. Я ожидал бы показать "In equals from TestEqual", потому что t3 является объектом TestEqual, поэтому должны вызываться equals from t1. Мое объяснение здесь состоит в том, что t1 является статической границей и рассматривается как объект, поэтому вызывается метод, унаследованный от класса Object, при этом параметр TestEqual t3 передается в объект. Но не значит ли это, что предыдущее объяснение из t1.equals(t2) неверно?
Для вызова метода в контексте перегрузки происходит наиболее специфичный вызов метода, который определяется во время компиляции. Правило выбора наиболее конкретного метода определено в спецификации языка Java 15.12.2.5. Выбор наиболее специфического метода: при другом обсуждении упомянутое утверждение:
Метод m1 строго более специфичен, чем другой метод m2, если и только если m1 более специфичен, чем m2, а m2 не более специфичен, чем m1.
Для объяснения вашего контекста, однако, давайте объявим два простых суперкласса и класса sup:
class SuperA
{
public void test(SuperA a)
{
System.out.println("super class's test() is called");
}
}
class SubB extends SuperA
{
public void test(SubB subB)
{
System.out.println("subclass's test() is called");
}
}
Теперь, если мы создадим два экземпляра таким образом:
SuperA obj = new SubB();
SubB obj2 = new SubB();
obj.test(obj2);
Вы увидите, что суперкласс test()
называется, потому что он определяется как более конкретный во время компиляции, и компилятор видит, что obj
это экземпляр типа SuperA
, Теперь брось obj
в SuubB
и вызвать test(obj2)
:
((SubB)obj).test(obj2); // cast to SubB
И это печать: "subclass's test() is called"
подразумевая, что это вызывает test(obj)
метод SubB
потому что на этот раз компилятор знает, что obj
имеет тип SubB
и наиболее конкретное разрешение для вызова test
,
Однако теперь давайте объявим два экземпляра следующим образом:
SuperA obj = new SubB();
SuperA obj2 = new SubB();
obj.test(obj2); // invokes super class's test method
((SubB)obj).test(obj2);// invokes super class's test method
((SubB)obj).test((SubB)obj2); // invoke sub class's test method
В этой серии вызовов первые два оператора будут вызывать суперкласс SuperA
Это тестовый метод, потому что эти вызовы более специфичны. Чтобы объяснить второй случай, однако:
((SubB)obj).test(obj2);// invokes super class's test method
На этот раз компилятор знает, что obj
имеет тип SubB
но он все еще видит, что obj2
имеет тип SperA
что более конкретно для test
вызов метода. Отсюда для второго примера, в котором оба obj
а также obj2
был объявлен с типом SuperA
: нам нужно, чтобы они оба SubB
вызывать SubB
"S test
метод.
Как Object
это супер класс всего класса в Java, вы должны понимать, однако equal
метод вызова системы вашего контекста. Чтобы избежать ошибок такого рода, вы увидите, что все реализованные equal
Метод в классах Java на самом деле переопределить equal
метод Object
класс и использовал instanceof
проверка. Например, равный метод реализации Integer
учебный класс:
public boolean equals(Object obj) {
if (obj instanceof Integer) { //<<<---- instance of checking
return value == ((Integer)obj).intValue();
}
return false;
}
Метод Object.equals(Object obj) принимает другой Object
Экземпляр в качестве параметра. Если вы определите свой TestEqual
как:
class TestEqual{
@override
public boolean equals(Object other ) {
System.out.println( "In equals from TestEqual" ); return false;
}
}
Это будет работать, как вы ожидаете.