Java: Llamar a un método super que llama a un método anulado

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

mi resultado esperado:

método de la subclase1
método de superclase1
método de superclase2

salida real:

método de la subclase1
método de superclase1
método de la subclase2

Sé técnicamente que he anulado un método público, pero pensé que porque llamaba al súper, cualquier llamada dentro del súper quedaría en el súper, esto no está sucediendo. ¿Alguna idea sobre cómo puedo hacer que suceda?

La palabra clave super no se “pega”. Cada llamada a un método se maneja de forma individual, por lo que incluso si llegaste a SuperClass.method1() llamando a super , eso no influye en ninguna otra llamada a método que puedas realizar en el futuro.

Eso significa que no hay forma directa de llamar a SuperClass.method2() desde SuperClass.method1() sin pasar por SubClass.method2() menos que esté trabajando con una instancia real de SuperClass .

Ni siquiera puede lograr el efecto deseado utilizando Reflection (consulte la documentación de java.lang.reflect.Method.invoke(Object, Object...) ).

[EDITAR] Todavía parece haber algo de confusión. Déjame probar una explicación diferente.

Cuando invocas foo() , en realidad this.foo() . Java simplemente te permite omitir this . En el ejemplo de la pregunta, el tipo de this es SubClass .

Entonces, cuando Java ejecuta el código en SuperClass.method1() , finalmente llega a this.method2();

El uso de super no cambia la instancia señalada por this . Entonces la llamada va a SubClass.method2() ya que this es de tipo SubClass .

Tal vez es más fácil de entender cuando imaginas que Java pasa this como un primer parámetro oculto:

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

Si sigue la stack de llamadas, puede ver que this nunca cambia, siempre es la instancia creada en main() .

Solo puede acceder a métodos anulados en los métodos de anulación (o en otros métodos de la clase de anulación).

Entonces: o bien no anula el method2() ni llama a super.method2() dentro de la versión anulada.

Está utilizando la palabra clave this que en realidad se refiere a la “instancia actualmente en ejecución del objeto que está utilizando”, es decir, está invocando this.method2(); en su superclase, es decir, llamará al método2 () en el objeto que está utilizando, que es la Subclase.

Si no desea que superClass.method1 llame a subClass.method2, haga que method2 sea privado para que no pueda anularse.

Aquí hay una sugerencia:

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

Si no funciona de esta manera, el polymorphism sería imposible (o al menos ni siquiera la mitad de útil).

Dado que la única forma de evitar que un método sea sobrescrito es utilizar la palabra clave super , he pensado mover el método2 () de SuperClass a otra nueva clase Base y luego llamarlo desde 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(); } } 

Salida:

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

Salida:

método de la subclase1
método de superclase1
método de superclase2

this siempre se refiere al objeto que se está ejecutando actualmente.

Para ilustrar mejor el punto aquí hay un bosquejo simple:

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

Si tiene una instancia de la caja externa, un objeto Subclass , donde sea que se aventure dentro de la caja, incluso en el ‘área’ de la Superclass , sigue siendo la instancia de la casilla exterior.

Además, en este progtwig solo se crea un objeto de las tres clases, por this que solo se puede referir a una cosa y es:

enter image description here

como se muestra en el ‘Heap Walker’ de Netbeans .

No creo que puedas hacerlo directamente. Una solución sería tener una implementación interna privada de method2 en la superclase, y llamar eso. Por ejemplo:

 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” palabra clave se refiere a la referencia de clase actual. Eso significa que, cuando se usa dentro del método, la clase ‘actual’ sigue siendo Subclase y, por lo tanto, se explica la respuesta.

Lo pienso de esta manera

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

Déjame mover esa subclase un poco hacia la izquierda para revelar lo que hay debajo ... (Hombre, me encantan los gráficos 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). 

En otras palabras, citando la Especificación del lenguaje Java :

El formulario super.Identifier hace referencia al campo denominado Identifier del objeto actual, pero con el objeto actual visto como una instancia de la superclase de la clase actual.

El formulario T.super.Identifier hace referencia al campo denominado Identifier de la instancia léxicamente envolvente correspondiente a T , pero con esa instancia vista como una instancia de la superclase de T

En términos simples, this es básicamente un objeto (* el ** objeto, el mismo objeto que puede moverse en variables), la instancia de la clase instanciada, una variable plana en el dominio de datos; super es como un puntero a un bloque prestado de código que desea que se ejecute, más como una mera llamada de función, y es relativo a la clase a la que se llama.

Por lo tanto, si usa super de la superclase obtiene el código de la clase superductora [abuelo] ejecutada), mientras que si usa this (o si se usa implícitamente) de una superclase, apunta hacia la subclase (porque nadie la ha modificado, y nadie podría).

En resumen, esto apunta al objeto actual y la invocación al método en Java es polimórfica por naturaleza. Entonces, la selección del método para la ejecución, depende totalmente del objeto apuntado por esto. Por lo tanto, al invocar el método method2 () desde la clase padre invoca al método2 () de la clase hija, ya que esto apunta al objeto de la clase hija. La definición de esto no cambia, independientemente de la clase que se use.

PD. a diferencia de los métodos, las variables miembro de la clase no son polimórficas.

Durante mi investigación para un caso similar, he terminado buscando en el seguimiento de la stack en el método de la subclase para averiguar de dónde viene la llamada. Probablemente haya formas más inteligentes de hacerlo, pero me funciona y es un enfoque dynamic.

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

Creo que la pregunta de tener una solución para el caso es razonable. Por supuesto, hay formas de resolver el problema con diferentes nombres de métodos o incluso diferentes tipos de parámetros, como ya se mencionó en el hilo, pero en mi caso no me gustaría confundir por diferentes nombres de métodos.