Java: chiamata un metodo super che chiama un metodo sottoposto a override

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(); } } 

la mia uscita prevista:

metodo sottoclass1
metodo superclass1
superclass method2

uscita effettiva:

metodo sottoclass1
metodo superclass1
metodo sottoclass2

So che tecnicamente ho superato un metodo pubblico, ma ho pensato che, poiché stavo chiamando il super, tutte le chiamate all’interno del super sarebbero rimaste nel super, questo non sta accadendo. Qualche idea su come posso farlo accadere?

La parola chiave super non “attacca”. Ogni chiamata al metodo viene gestita individualmente, quindi anche se hai ottenuto SuperClass.method1() chiamando super , ciò non influenza nessun altro metodo che potresti chiamare in futuro.

Ciò significa che non esiste un modo diretto per chiamare SuperClass.method2() da SuperClass.method1() senza passare a SubClass.method2() meno che non si stia lavorando con un’istanza reale di SuperClass .

Non è nemmeno ansible ottenere l’effetto desiderato utilizzando Reflection (consultare la documentazione di java.lang.reflect.Method.invoke(Object, Object...) ).

[EDIT] Sembra esserci ancora un po ‘di confusione. Lasciami provare una spiegazione diversa.

Quando invochi foo() , in realtà invochi this.foo() . Java ti consente semplicemente di omettere this . Nell’esempio della domanda, il tipo di this è SubClass .

Quindi, quando Java esegue il codice in SuperClass.method1() , alla fine arriva a this.method2();

L’uso di super non cambia l’istanza a cui fa riferimento. Quindi la chiamata va a SubClass.method2() poiché this è di tipo SubClass .

Forse è più facile capire quando si immagina che Java passi this come un primo parametro nascosto:

 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); } } 

Se segui lo stack delle chiamate, puoi vedere che this non cambia mai, è sempre l'istanza creata in main() .

È ansible accedere ai metodi sottoposti a override solo nei metodi di sovrascrittura (o in altri metodi della class di sovrascrittura).

Quindi: non sovrascrivere method2() o chiamare super.method2() all’interno della versione sovrascritta.

Stai utilizzando la parola chiave che in realtà si riferisce alla “istanza attualmente in esecuzione dell’object che stai utilizzando”, ovvero stai invocando this.method2(); sulla tua superclass, cioè, chiamerà il metodo 2 () sull’object che stai usando, che è la Sottoclassria.

Se non vuoi che superClass.method1 invochi subClass.method2, rendi private method2 in modo che non possa essere sovrascritto.

Ecco un suggerimento:

 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"); } } 

Se non funzionasse in questo modo, il polimorfismo sarebbe imansible (o almeno nemmeno la metà utile).

Poiché l’unico modo per evitare che un metodo venga superato è quello di utilizzare la parola chiave super , ho pensato di spostare il metodo2 () da SuperClass a un’altra nuova class Base e quindi chiamarlo da 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(); } } 

Produzione:

 subclass method1 superclass method1 superclass method2 
  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"); } } public class Demo { public static void main(String[] args) { SubClass mSubClass = new SubClass(); mSubClass.method1(); } } 

Produzione:

metodo sottoclass1
metodo superclass1
superclass method2

this si riferisce sempre all’attuale object in esecuzione.

Per illustrare ulteriormente il punto qui è un semplice schizzo:

 +----------------+ | Subclass | |----------------| | @method1() | | @method2() | | | | +------------+ | | | Superclass | | | |------------| | | | method1() | | | | method2() | | | +------------+ | +----------------+ 

Se hai un’istanza della scatola esterna, un object Subclass , dovunque ti trovi ad avventurarti all’interno della scatola, anche nella ‘zona’ della Superclass , è ancora l’istanza della scatola esterna.

Inoltre, in questo programma c’è un solo object che viene creato dalle tre classi, quindi this può riferirsi solo a una cosa ed è:

inserisci la descrizione dell'immagine qui

come mostrato nel Netbeans ‘Heap Walker’.

Non credo che tu possa farlo direttamente. Una soluzione alternativa sarebbe avere un’implementazione interna privata di method2 nella superclass e chiamarla. Per esempio:

 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"); } } 

“questa” parola chiave si riferisce al riferimento di class corrente. Ciò significa che, quando viene utilizzato all’interno del metodo, la class ‘current’ è ancora SubClass e quindi la risposta è spiegata.

Ci penso in questo modo

 +----------------+ | super | +----------------+ < -----------------+ | +------------+ | | | | this | | <-+ | | +------------+ | | | | | @method1() | | | | | | @method2() | | | | | +------------+ | | | | method4() | | | | method5() | | | +----------------+ | | We instantiate that class, not that one! 

Consentitemi di spostare quella sottoclass un po 'a sinistra per rivelare cosa c'è sotto ... (Man, I love love ASCII graphics)

 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). 

In altre parole, citando dalla specifica del linguaggio Java :

La forma super.Identifier riferisce al campo Identifier dell'object corrente, ma con l'object corrente visualizzato come un'istanza della superclass della class corrente.

Il modulo T.super.Identifier fa riferimento al campo Identifier dell'istanza che T.super.Identifier corrispondente a T , ma con quell'istanza visualizzata come un'istanza della superclass di T

In parole povere, this è fondamentalmente un object (* l'object **, lo stesso object che è ansible spostare in variabili), l'istanza della class istanziata, una variabile semplice nel dominio dei dati; super è come un puntatore a un blocco di codice preso in prestito che si desidera eseguire, più come una semplice chiamata di funzione, ed è relativo alla class in cui è chiamato.

Quindi se usi super dalla superclass ottieni codice dalla class superduper [il nonno] eseguito), mentre se usi this (o se è usato implicitamente) da una superclass continua a puntare alla sottoclass (perché nessuno lo ha cambiato - e nessuno potrebbe).

Per riassumere, questo punta all’object corrente e l’invocazione del metodo in java è polimorfica per sua natura. Quindi, la selezione del metodo per l’esecuzione dipende totalmente dall’object puntato da questo. Pertanto, invocando il metodo method2 () dalla class parent invoca il metodo2 () della class figlio, come questo punta all’object della class figlio. La definizione di questo non cambia, indipendentemente da quale class sia utilizzata.

PS. a differenza dei metodi, le variabili membro di class non sono polimorfiche.

Durante la mia ricerca per un caso simile, ho finito per controllare la traccia dello stack nel metodo sottoclass per scoprire da dove proviene la chiamata. Ci sono probabilmente modi più intelligenti per farlo, ma funziona per me ed è un approccio dinamico.

 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 } } 

Penso che la domanda per avere una soluzione per il caso sia ragionevole. Esistono naturalmente dei modi per risolvere il problema con nomi di metodi diversi o anche tipi di parametri diversi, come già menzionato nella discussione, ma nel mio caso non mi piace confondermi con nomi di metodi diversi.