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();
}
}
mój oczekiwany wynik:
podklasa method1
nadklasą method1
nadklasą Method2
rzeczywista wydajność:
podklasa method1
nadklasą method1
podklasa Method2
Wiem, technicznie rzecz biorąc, zastąpiłem publiczną metodę, ale pomyślałem, że skoro dzwonię do super, wszelkie połączenia z super pozostaną w super, to się nie dzieje. Jakieś pomysły, jak mogę to zrobić?
java
inheritance
overriding
super
jsonfry
źródło
źródło
Odpowiedzi:
Słowo kluczowe
super
nie „przykleja się”. Każde wywołanie metody jest traktowane indywidualnie, więc nawet jeśli masz doSuperClass.method1()
dzwoniącsuper
, że nie ma wpływu na wszelkie inne wywołanie metody, które można zrobić w przyszłości.Oznacza to, że nie istnieje bezpośredni sposób, aby zadzwonić
SuperClass.method2()
zSuperClass.method1()
bez przechodzenia choćSubClass.method2()
chyba że pracujesz z rzeczywistej instancjiSuperClass
.Nie możesz nawet osiągnąć pożądanego efektu za pomocą Reflection (patrz dokumentacja
java.lang.reflect.Method.invoke(Object, Object...)
).[EDYCJA] Wciąż wydaje się, że jest pewne zamieszanie. Spróbuję innego wyjaśnienia.
Kiedy wzywasz
foo()
, faktycznie wzywaszthis.foo()
. Java po prostu pozwala pominąć rozszerzeniethis
. W przykładzie w pytaniu typthis
toSubClass
.Więc kiedy Java wykonuje kod, w
SuperClass.method1()
końcu dociera dothis.method2();
Użycie
super
nie zmienia instancji wskazywanej przezthis
. Więc połączenie jest kierowane do,SubClass.method2()
ponieważthis
jest typuSubClass
.Może łatwiej jest to zrozumieć, wyobrażając sobie, że Java jest przekazywana
this
jako ukryty pierwszy parametr: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); } }
Jeśli podążasz za stosem wywołań, zobaczysz, że
this
nigdy się nie zmienia, zawsze jest to instancja utworzona wmain()
.źródło
super
. Za każdym razem, gdy wywołuje metodę, sprawdzi typ instancji i rozpocznie wyszukiwanie metody z tym typem, bez względu na to, jak często ją wywoływałeśsuper
. Więc kiedy wywołaszmethod2
instancjęSubClass
, zawsze zobaczy ona tę odSubClass
początku.super
nie zmienia instancji. Nie ustawia jakiegoś ukrytego pola „od teraz wszystkie wywołania metod powinny zacząć używaćSuperClass
”. Albo inaczej: wartośćthis
się nie zmienia.Dostęp do zastąpionych metod można uzyskać tylko w metodach przesłaniających (lub w innych metodach klasy przesłaniającej).
Tak więc: albo nie nadpisuj,
method2()
ani nie wywołujsuper.method2()
w nadpisanej wersji.źródło
Używasz
this
słowa kluczowego, które faktycznie odnosi się do "aktualnie działającej instancji obiektu, którego używasz", tothis.method2();
znaczy, że wywołujesz swoją nadklasę, to znaczy wywoła ona metodę method2 () na obiekcie, który ' ponownie używam, czyli podklasę.źródło
this
nie pomoże. Niekwalifikowane wezwanie pośrednio używathis
method2()
kompilator zobaczythis.method2()
. Więc nawet jeśli go usunieszthis
, nadal nie będzie działać. To, co mówi @Sean Patrick Floyd, jest poprawnethis
this
odnosi się do „konkretnej klasy uruchomionej instancji” (znanej w środowisku wykonawczym), a nie (jak zdaje się sądzić plakat) do „bieżącej klasy jednostki kompilacji” (gdzie używane jest słowo kluczowe, znane w czas kompilacji). Ale może też wprowadzać w błąd (jak wskazuje Shervin):this
odwołuje się również niejawnie za pomocą zwykłego wywołania metody;method2();
jest taki sam jakthis.method2();
Myślę o tym w ten sposób
+----------------+ | super | +----------------+ <-----------------+ | +------------+ | | | | this | | <-+ | | +------------+ | | | | | @method1() | | | | | | @method2() | | | | | +------------+ | | | | method4() | | | | method5() | | | +----------------+ | | We instantiate that class, not that one!
Pozwólcie, że przesunę tę podklasę trochę w lewo, aby pokazać, co jest pod spodem ... (Człowieku, uwielbiam grafikę 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).
Innymi słowy, cytując ze specyfikacji języka Java :
Mówiąc prościej,
this
jest w zasadzie obiektem (* obiekt **; ten sam obiekt, po którym można się poruszać w zmiennych), instancją klasy, której dotyczy instancja, zwykłą zmienną w domenie danych;super
jest jak wskaźnik do pożyczonego bloku kodu, który chcesz wykonać, bardziej przypomina zwykłe wywołanie funkcji i jest względny w stosunku do klasy, w której jest wywoływany.Dlatego jeśli używasz
super
z nadklasy, otrzymujesz kod z wykonanej klasy superduper [dziadek], podczas gdy jeśli używaszthis
(lub jeśli jest używana niejawnie) z nadklasy, wskazuje ona na podklasę (ponieważ nikt jej nie zmienił - i nikt mógłby).źródło
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"); } }
powołanie
SubClass mSubClass = new SubClass(); mSubClass.method1();
wyjścia
źródło
Jeśli nie chcesz, aby superClass.method1 wywoływał subClass.method2, ustaw metodę method2 jako prywatną, aby nie można jej było zastąpić.
Oto sugestia:
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"); } }
Gdyby to nie zadziałało w ten sposób, polimorfizm byłby niemożliwy (a przynajmniej nie w połowie tak użyteczny).
źródło
Ponieważ jedynym sposobem uniknięcia zastąpienia metody jest użycie słowa kluczowego super , pomyślałem, aby przenieść metodę method2 () z SuperClass w górę do innej nowej klasy bazowej, a następnie wywołać ją z 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(); } }
Wynik:
źródło
this
zawsze odnosi się do aktualnie wykonywanego obiektu.Aby lepiej zilustrować ten punkt, oto prosty szkic:
+----------------+ | Subclass | |----------------| | @method1() | | @method2() | | | | +------------+ | | | Superclass | | | |------------| | | | method1() | | | | method2() | | | +------------+ | +----------------+
Jeśli masz wystąpienie zewnętrznego pudełka,
Subclass
obiekt, gdziekolwiek zdarzy ci się zapuścić do środka pudełka, nawet doSuperclass
„obszaru”, nadal jest to wystąpienie zewnętrznego pudełka.Co więcej, w tym programie jest tylko jeden obiekt, który jest tworzony z trzech klas, więc zawsze
this
może odnosić się tylko do jednej rzeczy, a jest to:jak pokazano w Netbeans 'Heap Walker'.
źródło
Nie wierzę, że możesz to zrobić bezpośrednio. Jednym obejściem byłoby posiadanie prywatnej wewnętrznej implementacji method2 w superklasie i wywołanie tego. Na przykład:
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"); } }
źródło
Słowo kluczowe „this” odnosi się do aktualnej klasy. Oznacza to, że gdy jest używana wewnątrz metody, „bieżąca” klasa nadal jest podklasą, więc odpowiedź jest wyjaśniana.
źródło
Podsumowując, wskazuje to na bieżący obiekt, a wywołanie metody w Javie jest z natury polimorficzne. Zatem wybór metody do wykonania całkowicie zależy od wskazanego obiektu. Dlatego wywołanie metody method2 () z klasy nadrzędnej wywołuje metodę method2 () klasy potomnej, ponieważ this wskazuje na obiekt klasy potomnej. Definicja tego się nie zmienia, niezależnie od tego, której klasy jest używana.
PS. w przeciwieństwie do metod, zmienne składowe klasy nie są polimorficzne.
źródło
Podczas moich badań dotyczących podobnego przypadku kończyłem sprawdzaniem śladu stosu w metodzie podklasy, aby dowiedzieć się, skąd pochodzi wywołanie. Prawdopodobnie istnieją mądrzejsze sposoby, aby to zrobić, ale to działa i jest to dynamiczne podejście.
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 } }
Myślę, że pytanie o rozwiązanie tej sprawy jest rozsądne. Istnieją oczywiście sposoby rozwiązania problemu za pomocą różnych nazw metod lub nawet różnych typów parametrów, jak już wspomniano w wątku, ale w moim przypadku nie lubię mylić różnych nazw metod.
źródło
Jeszcze bardziej rozszerzone dane wyjściowe zadanego pytania, zapewni lepszy wgląd w specyfikator dostępu i zachowanie nadpisania.
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
źródło