public class SuperClass
{
public void method1()
{
System.out.println("superclass method1");
this.method2();
}
public void method2()
{
System.out.println("superclass method2");
}
}
public class SubClass extends SuperClass
{
@Override
public void method1()
{
System.out.println("subclass method1");
super.method1();
}
@Override
public void method2()
{
System.out.println("subclass method2");
}
}
public class Demo
{
public static void main(String[] args)
{
SubClass mSubClass = new SubClass();
mSubClass.method1();
}
}
мой ожидаемый результат:
подкласс method1
суперкласс method1
суперкласс method2
фактический выход:
подкласс метод1
суперкласс
метод1 подкласс метод2
Я знаю, что технически я переопределил общедоступный метод, но я подумал, что, поскольку я вызывал супер, любые вызовы внутри супер останутся в супер, этого не происходит. Есть идеи, как я могу это сделать?
java
inheritance
overriding
super
jsonfry
источник
источник
Ответы:
Ключевое слово
super
не «прилипает». Каждый вызов метода обрабатывается индивидуально, так что даже если вы получилиSuperClass.method1()
по телефонуsuper
, что не влияет на любой другой вызов метода , который вы могли бы сделать в будущем.Это означает, что прямого вызова
SuperClass.method2()
из негоSuperClass.method1()
нет, еслиSubClass.method2()
только вы не работаете с реальным экземпляромSuperClass
.Вы даже не можете добиться желаемого эффекта с помощью Reflection (см. Документацию
java.lang.reflect.Method.invoke(Object, Object...)
).[РЕДАКТИРОВАТЬ] По-прежнему существует некоторая путаница. Позвольте мне попробовать другое объяснение.
Когда вы вызываете
foo()
, вы действительно вызываетеthis.foo()
. Java просто позволяет опустить расширениеthis
. В примере в этом вопросе, типthis
являетсяSubClass
.Поэтому, когда Java выполняет код в
SuperClass.method1()
, он в конечном итоге достигаетthis.method2();
Использование
super
не изменяет экземпляр, на который указываетthis
. Таким образом, обращение идет к,SubClass.method2()
посколькуthis
имеет типSubClass
.Возможно, это легче понять, если представить, что Java передается
this
как скрытый первый параметр:public class SuperClass { public void method1(SuperClass this) { System.out.println("superclass method1"); this.method2(this); // <--- this == mSubClass } public void method2(SuperClass this) { System.out.println("superclass method2"); } } public class SubClass extends SuperClass { @Override public void method1(SubClass this) { System.out.println("subclass method1"); super.method1(this); } @Override public void method2(SubClass this) { System.out.println("subclass method2"); } } public class Demo { public static void main(String[] args) { SubClass mSubClass = new SubClass(); mSubClass.method1(mSubClass); } }
Если вы проследите за стеком вызовов, вы увидите, что он
this
никогда не меняется, это всегда экземпляр, созданный вmain()
.источник
super
. Каждый раз, когда он вызывает метод, он смотрит на тип экземпляра и начинает поиск метода с этим типом, независимо от того, как часто вы вызываетеsuper
. Поэтому, когда вы вызываетеmethod2
экземплярSubClass
, он всегда будет видеть тот, который былSubClass
первым.super
не меняет экземпляр. Он не устанавливает какое-то скрытое поле «с этого момента все вызовы методов должны начать использоватьSuperClass
». Или, другими словами: значениеthis
не меняется.Вы можете получить доступ только к переопределенным методам в методах переопределения (или в других методах замещающего класса).
Итак: либо не переопределяйте,
method2()
либо вызывайтеsuper.method2()
внутри переопределенной версии.источник
Вы используете
this
ключевое слово, которое фактически относится к «запущенному в данный момент экземпляру объекта, который вы используете», то есть вы вызываетеthis.method2();
свой суперкласс, то есть он вызовет метод2 () для объекта, который вы ' re using, который является подклассом.источник
this
тоже не поможет. Неквалифицированный вызов неявно используетthis
method2()
компилятор увидитthis.method2()
. Так что даже если вы удалитеthis
его, он все равно не сработает. То, что говорит @Sean Patrick Floyd, правильноthis
this
относится к «конкретному классу запущенного экземпляра» (известному во время выполнения), а не (как, кажется, полагает автор) к «текущему классу единицы компиляции» (где используется ключевое слово, известное в время компиляции). Но это также может вводить в заблуждение (как указывает Шервин): наthis
него также неявно ссылаются при вызове простого метода;method2();
совпадает сthis.method2();
Я так думаю об этом
+----------------+ | super | +----------------+ <-----------------+ | +------------+ | | | | this | | <-+ | | +------------+ | | | | | @method1() | | | | | | @method2() | | | | | +------------+ | | | | method4() | | | | method5() | | | +----------------+ | | We instantiate that class, not that one!
Позвольте мне сдвинуть этот подкласс немного влево, чтобы показать, что находится под ним ... (Чувак, я действительно люблю графику ASCII)
We are here | / +----------------+ | | super | v +----------------+ +------------+ | | this | | +------------+ | | @method1() | method1() | | @method2() | method2() | +------------+ method3() | | method4() | | method5() | +----------------+ Then we call the method over here... | +----------------+ _____/ | super | / +----------------+ | +------------+ | bar() | | | this | | foo() | | +------------+ | method0() | +-> | @method1() |--->| method1() | <------------------------------+ | @method2() | ^ | method2() | | +------------+ | | method3() | | | | method4() | | | | method5() | | | +----------------+ | \______________________________________ | \ | | | ...which calls super, thus calling the super's method1() here, so that that method (the overidden one) is executed instead[of the overriding one]. Keep in mind that, in the inheritance hierarchy, since the instantiated class is the sub one, for methods called via super.something() everything is the same except for one thing (two, actually): "this" means "the only this we have" (a pointer to the class we have instantiated, the subclass), even when java syntax allows us to omit "this" (most of the time); "super", though, is polymorphism-aware and always refers to the superclass of the class (instantiated or not) that we're actually executing code from ("this" is about objects [and can't be used in a static context], super is about classes).
Другими словами, цитата из спецификации языка Java :
С точки зрения непрофессионала,
this
это в основном объект (* объект **; тот же самый объект, который вы можете перемещать в переменных), экземпляр созданного класса, простая переменная в области данных;super
похож на указатель на заимствованный блок кода, который вы хотите выполнить, больше похож на простой вызов функции, и он относится к классу, в котором он вызывается.Поэтому, если вы используете
super
из суперкласса, вы получаете код из класса superduper [дедушка и бабушка], выполняемый), в то время как если вы используетеthis
(или если он используется неявно) из суперкласса, он продолжает указывать на подкласс (потому что никто его не изменил - и никто мог).источник
class SuperClass { public void method1() { System.out.println("superclass method1"); SuperClass se=new SuperClass(); se.method2(); } public void method2() { System.out.println("superclass method2"); } } class SubClass extends SuperClass { @Override public void method1() { System.out.println("subclass method1"); super.method1(); } @Override public void method2() { System.out.println("subclass method2"); } }
вызов
SubClass mSubClass = new SubClass(); mSubClass.method1();
выходы
источник
Если вы не хотите, чтобы superClass.method1 вызывал subClass.method2, сделайте method2 закрытым, чтобы его нельзя было переопределить.
Вот предложение:
public class SuperClass { public void method1() { System.out.println("superclass method1"); this.internalMethod2(); } public void method2() { // this method can be overridden. // It can still be invoked by a childclass using super internalMethod2(); } private void internalMethod2() { // this one cannot. Call this one if you want to be sure to use // this implementation. System.out.println("superclass method2"); } } public class SubClass extends SuperClass { @Override public void method1() { System.out.println("subclass method1"); super.method1(); } @Override public void method2() { System.out.println("subclass method2"); } }
Если бы это не сработало, полиморфизм был бы невозможен (или, по крайней мере, даже наполовину полезен).
источник
Поскольку единственный способ избежать переопределения метода - использовать ключевое слово super , я решил переместить метод 2 () из SuperClass в другой новый базовый класс, а затем вызвать его из SuperClass :
class Base { public void method2() { System.out.println("superclass method2"); } } class SuperClass extends Base { public void method1() { System.out.println("superclass method1"); super.method2(); } } class SubClass extends SuperClass { @Override public void method1() { System.out.println("subclass method1"); super.method1(); } @Override public void method2() { System.out.println("subclass method2"); } } public class Demo { public static void main(String[] args) { SubClass mSubClass = new SubClass(); mSubClass.method1(); } }
Выход:
источник
this
всегда относится к выполняющемуся в данный момент объекту.Чтобы проиллюстрировать это, вот простой набросок:
+----------------+ | Subclass | |----------------| | @method1() | | @method2() | | | | +------------+ | | | Superclass | | | |------------| | | | method1() | | | | method2() | | | +------------+ | +----------------+
Если у вас есть экземпляр внешнего бокса,
Subclass
объект, куда бы вы ни пошли, внутри бокса, даже вSuperclass
«область», он все равно остается экземпляром внешнего бокса.Более того, в этой программе есть только один объект, который создается из трех классов, поэтому
this
может ссылаться только на одну вещь, а именно:как показано в « Обходчике кучи» Netbeans .
источник
Я не верю, что вы можете сделать это напрямую. Одним из обходных путей было бы иметь частную внутреннюю реализацию method2 в суперклассе и вызывать ее. Например:
public class SuperClass { public void method1() { System.out.println("superclass method1"); this.internalMethod2(); } public void method2() { this.internalMethod2(); } private void internalMethod2() { System.out.println("superclass method2"); } }
источник
Ключевое слово "this" относится к текущей ссылке на класс. Это означает, что когда он используется внутри метода, «текущий» класс по-прежнему является подклассом, поэтому ответ объясняется.
источник
Подводя итог, это указывает на текущий объект, а вызов метода в java по своей природе полиморфен. Итак, выбор метода для выполнения полностью зависит от объекта, на который он указывает. Следовательно, вызов метода method2 () из родительского класса вызывает метод method2 () дочернего класса, так как this указывает на объект дочернего класса. Определение этого не меняется, независимо от того, какой класс он используется.
PS. в отличие от методов, переменные-члены класса не полиморфны.
источник
Во время моего исследования аналогичного случая я закончил проверкой трассировки стека в методе подкласса, чтобы выяснить, откуда исходит вызов. Возможно, есть более разумные способы сделать это, но для меня это работает, и это динамический подход.
public void method2(){ Exception ex=new Exception(); StackTraceElement[] ste=ex.getStackTrace(); if(ste[1].getClassName().equals(this.getClass().getSuperclass().getName())){ super.method2(); } else{ //subclass method2 code } }
Я думаю, что вопрос о разрешении дела является разумным. Конечно, есть способы решить проблему с разными именами методов или даже разными типами параметров, как уже упоминалось в потоке, но в моем случае я не хочу путать разные имена методов.
источник
Более расширенный вывод поднятого вопроса даст больше информации о спецификаторе доступа и поведении переопределения.
package overridefunction; public class SuperClass { public void method1() { System.out.println("superclass method1"); this.method2(); this.method3(); this.method4(); this.method5(); } public void method2() { System.out.println("superclass method2"); } private void method3() { System.out.println("superclass method3"); } protected void method4() { System.out.println("superclass method4"); } void method5() { System.out.println("superclass method5"); } } package overridefunction; public class SubClass extends SuperClass { @Override public void method1() { System.out.println("subclass method1"); super.method1(); } @Override public void method2() { System.out.println("subclass method2"); } // @Override private void method3() { System.out.println("subclass method3"); } @Override protected void method4() { System.out.println("subclass method4"); } @Override void method5() { System.out.println("subclass method5"); } } package overridefunction; public class Demo { public static void main(String[] args) { SubClass mSubClass = new SubClass(); mSubClass.method1(); } } subclass method1 superclass method1 subclass method2 superclass method3 subclass method4 subclass method5
источник