Супер ключевое слово в Java, интересное поведение, пожалуйста, объясните
Допустим, что у нас есть следующий код:
class A {
public void doLogic() {
System.out.println("doLogic from A");
}
}
class B extends A {
@Override
public void doLogic() {
System.out.println("doLogic from B");
}
public void doDifferentLogic() {
System.out.println("doDifferentLogic from B");
super.doLogic();
}
}
class C extends B {
@Override
public void doLogic() {
System.out.println("doLogic from C");
}
}
public class Test {
public static void main(String[] args) {
C c = new C();
c.doDifferentLogic();
}
}
Когда мы выполняем этот код, ожидаемое поведение выглядит следующим образом:
Так как c содержит ссылку на объект класса C, когда вы вызываете метод c.doDifferentLogic()
, JVM ищет метод в классе C и, поскольку он не найден, он начинает искать дерево наследования. Как и ожидалось, метод doDifferentLogic()
находится в суперклассе и выполняется. Однако ожидается, что конструкция super.doLogic()
будет выглядеть из текущей ссылки "Точка зрения", которая имеет тип C. Таким образом, супер C должен быть B, но вместо этого вызывается метод из верхнего класса A.
Если вы удалите ключевое слово super
или замените его на ключевое слово this
(то же самое, что и "this" неявно), вы получите ожидаемое полиморфное поведение и вызывается doLogic()
из класса C.
Итак, мой вопрос:
Должен вызывать super.doLogic()
be this.super.doLogic()
(2) вместо static.super.doLogic()
(1)?
Оба являются недопустимыми конструкциями, они здесь просто для того, чтобы лучше объяснить себя.
(1) или другими словами - из ссылки на текущий объект c, получить суперкласс текущего объекта и вызывать метод doLogic()
вместо
(2) из этого класса получить суперкласс и вызвать его метод doLogic()
?
Ответы
Ответ 1
В Java ключевое слово super
всегда ссылается на суперкласс класса, в котором используется ключевое слово, а не на суперкласс динамического типа объекта, на который вызывается метод. Другими словами, super
разрешается статически, а не динамически. Это означает, что в контексте класса B
ключевое слово super
всегда относится к классу A
, независимо от того, выполняется ли метод B
с использованием объекта C
в качестве получателя. Насколько я знаю, нет способа динамически определять тип суперкласса и использовать его методы без использования отражения.
Надеюсь, это поможет!
Ответ 2
Здесь JLS определяет это специально:
- Если форма супер. NonWildTypeArguments opt Идентификатор, тогда имя метода является Идентификатором, а класс, подлежащий поиску, является суперклассом класса, декларация которого содержит вызов метода.
Итак, Java рассматривает super
как относящуюся к суперклассу класса, содержащего вызов super.method
, а не к текущему типу времени выполнения.
Ответ 3
Всякий раз, когда выполняется super.doLogic() из класса B, он всегда будет ссылаться на метод doLogic() суперкласса класса B, который в этом случае будет classA. Это желаемое поведение, так что управление не будет проходить между классами внутри одного и того же метода. Это концепция контекста класса. Когда вы находитесь в контексте класса, вам нужно будет следовать правилам, установленным этим классом, и не продолжать передавать управление между различными контекстами.