method make implement how create java superclass

make - ¿Por qué es super.super.method(); no permitido en Java?



super(); (22)

@Jon Skeet Bonita explicación. OMI: si alguien quiere llamar al método super.super, entonces uno debe querer ignorar el comportamiento del padre inmediato, pero quiere acceder al comportamiento del padre principal. Esto se puede lograr a través de la instancia de. Como el código de abajo

public class A { protected void printClass() { System.out.println("In A Class"); } } public class B extends A { @Override protected void printClass() { if (!(this instanceof C)) { System.out.println("In B Class"); } super.printClass(); } } public class C extends B { @Override protected void printClass() { System.out.println("In C Class"); super.printClass(); } }

Aquí está la clase de conductor,

public class Driver { public static void main(String[] args) { C c = new C(); c.printClass(); } }

La salida de esto será

In C Class In A Class

El comportamiento de PrintClass de clase B se ignorará en este caso. No estoy seguro de si esta es una práctica ideal o ideal para lograr super.super, pero aún así está funcionando.

Leí esta pregunta y pensé que podría resolverse fácilmente (no que no se pueda resolver sin ella) si pudiera escribir:

@Override public String toString() { return super.super.toString(); }

No estoy seguro de si es útil en muchos casos, pero me pregunto por qué no lo es y si existe algo así en otros idiomas.

¿Qué piensan ustedes?

EDIT: Para aclarar: sí, lo sé, eso es imposible en Java y realmente no lo echo de menos. Esto no es nada de lo que esperaba que funcionara y me sorprendió recibir un error del compilador. Acabo de tener la idea y me gustaría discutirla.


Además de los buenos puntos que otros han hecho, creo que hay otra razón: ¿qué sucede si la superclase no tiene una superclase?

Dado que cada clase naturalmente extiende (al menos) Object , super.whatever() siempre se referirá a un método en la superclase. Pero, ¿qué pasaría si su clase solo amplia el Object ? ¿Cómo debe manejarse ese comportamiento: un error del compilador, un NullPointer, etc.?

Creo que la razón principal por la que esto no está permitido es que viola la encapsulación, pero esto también puede ser una pequeña razón.


Creo que Jon Skeet tiene la respuesta correcta. Solo me gustaría agregar que puede acceder a las variables sombreadas desde las superclases de las superclases al emitir this :

interface I { int x = 0; } class T1 implements I { int x = 1; } class T2 extends T1 { int x = 2; } class T3 extends T2 { int x = 3; void test() { System.out.println("x=/t/t" + x); System.out.println("super.x=/t/t" + super.x); System.out.println("((T2)this).x=/t" + ((T2)this).x); System.out.println("((T1)this).x=/t" + ((T1)this).x); System.out.println("((I)this).x=/t" + ((I)this).x); } } class Test { public static void main(String[] args) { new T3().test(); } }

que produce la salida:

x= 3 super.x= 2 ((T2)this).x= 2 ((T1)this).x= 1 ((I)this).x= 0

(ejemplo de la JLS )

Sin embargo, esto no funciona para las llamadas de método porque las llamadas de método se determinan en función del tipo de tiempo de ejecución del objeto.


Creo que el siguiente código permite usar super.super ... super.method () en la mayoría de los casos. (Incluso si es feo hacer eso)

En breve

  1. crear instancia temporal de tipo ancestro
  2. Copia los valores de los campos del objeto original al temporal.
  3. invocar el método de destino en objeto temporal
  4. copiar los valores modificados de nuevo al objeto original

Uso:

public class A { public void doThat() { ... } } public class B extends A { public void doThat() { /* don''t call super.doThat() */ } } public class C extends B { public void doThat() { Magic.exec(A.class, this, "doThat"); } } public class Magic { public static <Type, ChieldType extends Type> void exec(Class<Type> oneSuperType, ChieldType instance, String methodOfParentToExec) { try { Type type = oneSuperType.newInstance(); shareVars(oneSuperType, instance, type); oneSuperType.getMethod(methodOfParentToExec).invoke(type); shareVars(oneSuperType, type, instance); } catch (Exception e) { throw new RuntimeException(e); } } private static <Type, SourceType extends Type, TargetType extends Type> void shareVars(Class<Type> clazz, SourceType source, TargetType target) throws IllegalArgumentException, IllegalAccessException { Class<?> loop = clazz; do { for (Field f : loop.getDeclaredFields()) { if (!f.isAccessible()) { f.setAccessible(true); } f.set(target, f.get(source)); } loop = loop.getSuperclass(); } while (loop != Object.class); } }


Creo que este es un problema que rompe el acuerdo de herencia.
Al extender una clase obedeces / aceptas su comportamiento, características
Mientras llama a super.super.method() , desea romper su propio acuerdo de obediencia.

Simplemente no puedes elegir la súper clase .

Sin embargo, pueden ocurrir situaciones en las que sientas la necesidad de llamar a super.super.method() , ¡generalmente un signo de diseño incorrecto, en tu código o en el código que heredas!
Si las clases súper y súper súper no se pueden refactorizar (algún código heredado), entonces opte por la composición sobre la herencia.

La ruptura de la encapsulación se produce cuando se omiten algunos métodos al romper el código encapsulado. Los métodos diseñados para no ser anulados se marcan como finales .


Creo que si sobrescribes un método y quieres la versión super-class del mismo (por ejemplo, decir para equals ), entonces casi siempre quieres llamar primero a la versión de superclase directa, cuál llamará a su versión de superclase a su vez si quiere.

Creo que solo tiene sentido (si es que lo hago. No puedo pensar en un caso en el que lo tenga) llamar a una superclase arbitraria de la versión de un método. No sé si eso es posible en Java. Se puede hacer en C ++:

this->ReallyTheBase::foo();


En C # puedes llamar a un método de cualquier ancestro como este:

public class A internal virtual void foo() ... public class B : A public new void foo() ... public class C : B public new void foo() { (this as A).foo(); }

También puedes hacer esto en Delphi:

type A=class procedure foo; ... B=class(A) procedure foo; override; ... C=class(B) procedure foo; override; ... A(objC).foo();

Pero en Java puedes hacer ese enfoque solo con algo de equipo. Una forma posible es:

class A { int y=10; void foo(Class X) throws Exception { if(X!=A.class) throw new Exception("Incorrect parameter of "+this.getClass().getName()+".foo("+X.getName()+")"); y++; System.out.printf("A.foo(%s): y=%d/n",X.getName(),y); } void foo() throws Exception { System.out.printf("A.foo()/n"); this.foo(this.getClass()); } } class B extends A { int y=20; @Override void foo(Class X) throws Exception { if(X==B.class) { y++; System.out.printf("B.foo(%s): y=%d/n",X.getName(),y); } else { System.out.printf("B.foo(%s) calls B.super.foo(%s)/n",X.getName(),X.getName()); super.foo(X); } } } class C extends B { int y=30; @Override void foo(Class X) throws Exception { if(X==C.class) { y++; System.out.printf("C.foo(%s): y=%d/n",X.getName(),y); } else { System.out.printf("C.foo(%s) calls C.super.foo(%s)/n",X.getName(),X.getName()); super.foo(X); } } void DoIt() { try { System.out.printf("DoIt: foo():/n"); foo(); Show(); System.out.printf("DoIt: foo(B):/n"); foo(B.class); Show(); System.out.printf("DoIt: foo(A):/n"); foo(A.class); Show(); } catch(Exception e) { //... } } void Show() { System.out.printf("Show: A.y=%d, B.y=%d, C.y=%d/n/n", ((A)this).y, ((B)this).y, ((C)this).y); } }

Resultado de objC.DoIt ():

DoIt: foo(): A.foo() C.foo(C): y=31 Show: A.y=10, B.y=20, C.y=31 DoIt: foo(B): C.foo(B) calls C.super.foo(B) B.foo(B): y=21 Show: A.y=10, B.y=21, C.y=31 DoIt: foo(A): C.foo(A) calls C.super.foo(A) B.foo(A) calls B.super.foo(A) A.foo(A): y=11 Show: A.y=11, B.y=21, C.y=31


En mi opinión, es una forma limpia de lograr el comportamiento super.super.sayYourName() en Java.

public class GrandMa { public void sayYourName(){ System.out.println("Grandma Fedora"); } } public class Mama extends GrandMa { public void sayYourName(boolean lie){ if(lie){ super.sayYourName(); }else { System.out.println("Mama Stephanida"); } } } public class Daughter extends Mama { public void sayYourName(boolean lie){ if(lie){ super.sayYourName(lie); }else { System.out.println("Little girl Masha"); } } } public class TestDaughter { public static void main(String[] args){ Daughter d = new Daughter(); System.out.print("Request to lie: d.sayYourName(true) returns "); d.sayYourName(true); System.out.print("Request not to lie: d.sayYourName(false) returns "); d.sayYourName(false); } }

Salida:

Request to lie: d.sayYourName(true) returns Grandma Fedora
Request not to lie: d.sayYourName(false) returns Little girl Masha


En una conjetura, porque no se utiliza tan a menudo. La única razón por la que podría ver su uso es si su padre directo ha anulado alguna funcionalidad y está intentando restaurarla al original.

Lo que me parece estar en contra de los principios de OO, ya que el padre directo de la clase debería estar más estrechamente relacionado con su clase que el abuelo.


Es simplemente fácil de hacer. Por ejemplo:

C subclase de B y B subclase de A. Los tres tienen método methodName (), por ejemplo.

clase abstracta pública A

public void methodName() { System.out.println("Class A"); }

}

clase pública B extiende A {

public void methodName() { super.methodName(); System.out.println("Class B"); } // Will call the super methodName public void hackSuper() { super.methodName(); }

}

clase pública C extiende B {

public static void main(String[] args) { A a = new C(); a.methodName(); } @Override public void methodName() { /*super.methodName();*/ hackSuper(); System.out.println("Class C"); }

}

Ejecutar clase C Salida será: Clase A Clase C

En lugar de salida: Clase A Clase B Clase C


Hay algunas buenas razones para hacer esto. Es posible que tenga una subclase que tiene un método que se implementa incorrectamente, pero el método principal se implementa correctamente. Debido a que pertenece a una biblioteca de terceros, es posible que no pueda o no quiera cambiar la fuente. En este caso, desea crear una subclase pero anular un método para llamar al método super.super.

Como lo muestran otros carteles, es posible hacerlo a través de la reflexión, pero debería ser posible hacer algo como

(SuperSuperClass this) .theMethod ();

Estoy lidiando con este problema en este momento: la solución rápida es copiar y pegar el método de superclase en el método de subclase :)


He tenido situaciones como estas cuando la arquitectura consiste en crear una funcionalidad común en una CustomBaseClass común que se implementa en nombre de varias clases derivadas. Sin embargo, debemos sortear la lógica común para un método específico para una clase derivada específica. En tales casos, debemos usar una implementación super.super.methodX.

Esto lo logramos mediante la introducción de un miembro booleano en CustomBaseClass, que puede usarse para diferir selectivamente la implementación personalizada y ceder a la implementación de la estructura predeterminada cuando sea conveniente.

... FrameworkBaseClass (....) extends... { methodA(...){...} methodB(...){...} ... methodX(...) ... methodN(...){...} } /* CustomBaseClass overrides default framework functionality for benefit of several derived classes.*/ CustomBaseClass(...) extends FrameworkBaseClass { private boolean skipMethodX=false; /* implement accessors isSkipMethodX() and setSkipMethodX(boolean)*/ methodA(...){...} methodB(...){...} ... methodN(...){...} methodX(...){ if (isSkipMethodX()) { setSKipMethodX(false); super.methodX(...); return; } ... //common method logic } } DerivedClass1(...) extends CustomBaseClass DerivedClass2(...) extends CustomBaseClass ... DerivedClassN(...) extends CustomBaseClass... DerivedClassX(...) extends CustomBaseClass... { methodX(...){ super.setSKipMethodX(true); super.methodX(...); } }

Sin embargo, si se siguen buenos principios de arquitectura tanto en el marco como en la aplicación, podríamos evitar tales situaciones fácilmente, utilizando el enfoque hasA, en lugar del enfoque isA. Pero en todo momento no es muy práctico esperar una arquitectura bien diseñada en su lugar, y de ahí la necesidad de alejarse de los principios de diseño sólidos e introducir trucos como este. Sólo mis 2 centavos ...


La palabra clave super es solo una forma de invocar el método en la superclase. En el tutorial de Java: https://docs.oracle.com/javase/tutorial/java/IandI/super.html

Si su método anula uno de los métodos de su superclase, puede invocar el método anulado mediante el uso de la palabra clave super.

No creas que es una referencia del súper objeto !!! No, es solo una palabra clave para invocar métodos en la superclase.

Aquí hay un ejemplo:

class Animal { public void doSth() { System.out.println(this); // It''s a Cat! Not an animal! System.out.println("Animal do sth."); } } class Cat extends Animal { public void doSth() { System.out.println(this); System.out.println("Cat do sth."); super.doSth(); } }

Cuando llama a cat.doSth() , el método doSth() en la clase Animal imprimirá this y es un gato.


Llamar a super.super.method () tiene sentido cuando no puede cambiar el código de la clase base. Esto sucede a menudo cuando se está extendiendo una biblioteca existente.

Pregúntate primero, ¿por qué estás extendiendo esa clase? Si la respuesta es "porque no puedo cambiarla", puede crear un paquete y una clase exactos en su aplicación y reescribir el método travieso o crear un delegado:

package com.company.application; public class OneYouWantExtend extends OneThatContainsDesiredMethod { // one way is to rewrite method() to call super.method() only or // to doStuff() and then call super.method() public void method() { if (isDoStuff()) { // do stuff } super.method(); } protected abstract boolean isDoStuff(); // second way is to define methodDelegate() that will call hidden super.method() public void methodDelegate() { super.method(); } ... } public class OneThatContainsDesiredMethod { public void method() {...} ... }

Por ejemplo, puede crear la clase org.springframework.test.context.junit4.SpringJUnit4ClassRunner en su aplicación, por lo que esta clase debe cargarse antes que la real del archivo jar. Luego reescribir métodos o constructores.

Atención: este es un pirateo absoluto, y NO se recomienda su uso, ¡pero está FUNCIONANDO! El uso de este enfoque es peligroso debido a posibles problemas con los cargadores de clases. Además, esto puede causar problemas cada vez que actualice la biblioteca que contiene la clase sobrescrita.


Mira this proyecto Github, especialmente la variable objectHandle. Este proyecto muestra cómo llamar de manera efectiva y precisa al método de abuelo en un nieto.

En caso de que el enlace se rompa, aquí está el código:

import lombok.val; import org.junit.Assert; import org.junit.Test; import java.lang.invoke.*; /* Your scientists were so preoccupied with whether or not they could, they didn’t stop to think if they should. Please don''t actually do this... :P */ public class ImplLookupTest { private MethodHandles.Lookup getImplLookup() throws NoSuchFieldException, IllegalAccessException { val field = MethodHandles.Lookup.class.getDeclaredField("IMPL_LOOKUP"); field.setAccessible(true); return (MethodHandles.Lookup) field.get(null); } @Test public void test() throws Throwable { val lookup = getImplLookup(); val baseHandle = lookup.findSpecial(Base.class, "toString", MethodType.methodType(String.class), Sub.class); val objectHandle = lookup.findSpecial(Object.class, "toString", MethodType.methodType(String.class), // Must use Base.class here for this reference to call Object''s toString Base.class); val sub = new Sub(); Assert.assertEquals("Sub", sub.toString()); Assert.assertEquals("Base", baseHandle.invoke(sub)); Assert.assertEquals(toString(sub), objectHandle.invoke(sub)); } private static String toString(Object o) { return o.getClass().getName() + "@" + Integer.toHexString(o.hashCode()); } public class Sub extends Base { @Override public String toString() { return "Sub"; } } public class Base { @Override public String toString() { return "Base"; } } }

Feliz codificacion !!!!


No tengo suficiente reputación para comentar, así que agregaré esto a las otras respuestas.

Jon Skeet responde excelentemente, con un bello ejemplo. Matt B tiene un punto: no todas las superclases tienen supers. Tu código se rompería si llamaras a un súper de un súper que no tuviera un súper.

La programación orientada a objetos (que es Java) se trata de objetos, no de funciones. Si desea programación orientada a tareas, elija C ++ o algo más. Si su objeto no encaja en su súper clase, entonces necesita agregarlo a la "clase de abuelos", crear una nueva clase, o encontrar otro súper en el que encaje.

Personalmente, he encontrado que esta limitación es una de las mayores fortalezas de Java. El código es algo rígido en comparación con otros idiomas que he usado, pero siempre sé qué esperar. Esto ayuda con el objetivo "simple y familiar" de Java. En mi opinión, llamar a super.super no es simple ni familiar. Tal vez los desarrolladores sintieron lo mismo?



Si cree que va a necesitar la superclase, puede hacer referencia en una variable para esa clase. Por ejemplo:

public class Foo { public int getNumber() { return 0; } } public class SuperFoo extends Foo { public static Foo superClass = new Foo(); public int getNumber() { return 1; } } public class UltraFoo extends Foo { public static void main(String[] args) { System.out.println(new UltraFoo.getNumber()); System.out.println(new SuperFoo().getNumber()); System.out.println(new SuperFoo().superClass.getNumber()); } public int getNumber() { return 2; } }

Debe imprimir:

2 1 0


Viola la encapsulación. No deberías poder pasar por alto el comportamiento de la clase padre. Tiene sentido a veces poder evitar el comportamiento de su propia clase (particularmente desde el mismo método) pero no el de sus padres. Por ejemplo, supongamos que tenemos una "colección de artículos" base, una subclase que representa "una colección de artículos rojos" y una subclase de la que representa "una colección de artículos rojos grandes". Tiene sentido tener:

public class Items { public void add(Item item) { ... } } public class RedItems extends Items { @Override public void add(Item item) { if (!item.isRed()) { throw new NotRedItemException(); } super.add(item); } } public class BigRedItems extends RedItems { @Override public void add(Item item) { if (!item.isBig()) { throw new NotBigItemException(); } super.add(item); } }

Eso está bien: RedItems siempre puede estar seguro de que los elementos que contiene son todos rojos. Ahora supongamos que pudimos llamar a super.super.add ():

public class NaughtyItems extends RedItems { @Override public void add(Item item) { // I don''t care if it''s red or not. Take that, RedItems! super.super.add(item); } }

Ahora podríamos agregar lo que RedItems , y el invariante en RedItems está roto.

¿Tiene sentido?


Yo pondría el cuerpo del método super.super en otro método, si es posible

class SuperSuperClass { public String toString() { return DescribeMe(); } protected String DescribeMe() { return "I am super super"; } } class SuperClass extends SuperSuperClass { public String toString() { return "I am super"; } } class ChildClass extends SuperClass { public String toString() { return DescribeMe(); } }

O si no puedes cambiar la clase super-super, puedes probar esto:

class SuperSuperClass { public String toString() { return "I am super super"; } } class SuperClass extends SuperSuperClass { public String toString() { return DescribeMe(super.toString()); } protected String DescribeMe(string fromSuper) { return "I am super"; } } class ChildClass extends SuperClass { protected String DescribeMe(string fromSuper) { return fromSuper; } }

En ambos casos, la

new ChildClass().toString();

resultados a "soy super super"


public class A { @Override public String toString() { return "A"; } } public class B extends A { @Override public String toString() { return "B"; } } public class C extends B { @Override public String toString() { return "C"; } } public class D extends C { @Override public String toString() { String result = ""; try { result = this.getClass().getSuperclass().getSuperclass().getSuperclass().newInstance().toString(); } catch (InstantiationException ex) { Logger.getLogger(D.class.getName()).log(Level.SEVERE, null, ex); } catch (IllegalAccessException ex) { Logger.getLogger(D.class.getName()).log(Level.SEVERE, null, ex); } return result; } } public class Main { public static void main(String... args) { D d = new D(); System.out.println(d); } }

ejecutar: A BUILD SUCCESSFUL (tiempo total: 0 segundos)


public class SubSubClass extends SubClass { @Override public void print() { super.superPrint(); } public static void main(String[] args) { new SubSubClass().print(); } } class SuperClass { public void print() { System.out.println("Printed in the GrandDad"); } } class SubClass extends SuperClass { public void superPrint() { super.print(); } }

Salida: Impreso en la GrandDad.