java polymorphism

java - Parece que no se puede entender el polimorfismo complejo



polymorphism (5)

Explicación

public class AA { private void foo() { ... } ^^^^^^^ }

El polimorfismo no se aplica a private métodos private . Una subclase no hereda private métodos private , por lo que no se pueden anular:

Una clase C hereda de su superclase directa todos los métodos concretos m (tanto estáticos como de instancia) de la superclase para los que se cumplen todos los siguientes:

  • m es un miembro de la superclase directa de C
  • m es public , protected o declarado con acceso al paquete en el mismo paquete que C
  • Ningún método declarado en C tiene una firma que es una firma de firma de m .

Especificación del lenguaje Java - 8.4.8. Herencia, anulación y ocultación

Por lo tanto, la llamada foo() del constructor A no invoca BB#foo , llama a AA#foo .

Pero la llamada goo() dentro de AA#foo refiere al método anulado BB#goo . Aquí, con métodos public , se aplicaron métodos de anulación y polimorfismo.

Es un poco complicado, por lo que te recomendaría que pongas la anotación @Override todos los @Override donde se supone que debe estar.

public class BB extends AA { @Override // it doesn''t compile - no overriding here public void foo() { ... } @Override // it does override public void goo() { ... } }

También podría ser útil detectar otro problema:

En ocasiones, los programadores sobrecargan una declaración de método cuando pretenden anularla , lo que lleva a problemas sutiles. El tipo de anotación Override admite la detección temprana de dichos problemas.

Si una declaración de método en el tipo T se anota con @Override , pero el método no anula desde T un método declarado en un supertipo de T , o si no es equivalente a un método público de Object , entonces se produce un error en tiempo de compilación .

Especificación del lenguaje Java - 9.6.4.4. @Override

Ilustración

Si el cuerpo de un constructor no comienza con una invocación explícita del constructor y el constructor que se está declarando no es parte de la clase primordial Object , entonces el cuerpo del constructor comienza implícitamente con una superclase constructor invocation super(); , una invocación del constructor de su superclase directa que no toma argumentos.

Especificación del lenguaje Java - 8.8.7. Cuerpo Constructor

Para hacerlo mas simple,

public BB() { foo(); }

se convierte en

public BB() { super(); foo(); }

Manteniendo super(); En mente, podemos hacer la siguiente ilustración:

new BB() AA() // super(); -> AA constructor A#foo() // private method call B#goo() // polymorphic method call BB() // BB constructor B#foo() // plain method call

Estoy estudiando CS y tenemos preguntas sobre el polimorfismo que no puedo envolver en mi mente. Aquí hay un ejemplo:

public class AA{ public AA(){ foo(); } private void foo() { System.out.print("AA::foo "); goo(); } public void goo(){ System.out.print("AA::goo "); } } public class BB extends AA{ public BB(){ foo(); } public void foo(){ System.out.print("BB:foo "); } public void goo(){ System.out.print("BB::goo "); } public static void main(String[] args){ // Code goes here } }

Cuando en void main añado la línea:

AA a = new BB();

primero va el constructor AA imprime AA: foo pero luego goo () lo envía al goo de BB, ¿por qué?

El polimorfismo simple como "Animal -> cat / spider / dog" es fácil de entender, pero cuando se trata de esto, simplemente estoy perdido. ¿Pueden darme algunos consejos sobre cómo leer este código? ¿Cuáles son las reglas?

EDITAR : no hay anotación de @Override porque esta es una pregunta de un examen.


AA :: foo () es privado, por lo que AA :: foo () y BB :: foo () no son lo mismo.

new BB()

llamará primero a AA :: Constructor, y llamará a AA :: foo ().

AA :: foo () llama a goo () y desde que creaste una clase de BB será BB :: goo ().

Utilice la palabra clave "sobrescribir" en el método cuando desee hacer algo como esto


El polimorfismo es simple pero confuso a veces cuando usamos diferentes conjuntos de nombres [lo que es el caso aquí].

El polimorfismo es básicamente una relación padre-hijo. La clave aquí es, si está tratando de ubicar los nombres de las clases, en su lugar, use la línea de comentarios junto a los nombres de las clases como se muestra a continuación.

public class AA{} //your Parent name public class BB extends AA{} // yourself i.e. your name

Cuando se trata de un código como este, AA a = new BB(); , decodifique el código de la siguiente manera:

BB eres tú, AA es tu padre.

new palabra clave es con USTED (es decir, BB), por lo que se creará o nacerá un nuevo objeto de YOU. Para que USTED pueda nacer, sin sus padres (es decir, AA), no puede existir y, por lo tanto, primero nacerán o se crearán (es decir, el constructor de AA se ejecutaría). Una vez que se crean tus padres (es decir, AA), entonces es el momento de que TÚ nacas (es decir, BB constructor se ejecutaría).

En tu ejemplo,

public AA(){ foo(); -- line A } private void foo() { System.out.print("AA::foo "); goo(); -- line B } public void goo(){ System.out.print("AA::goo "); -- line C }

Como dije antes, la línea A se llamaría cuando dices AA a = new BB(); como la Línea A está en el Constructor de AA, la Línea A llama al método foo () y así el control cae en foo (), imprime "AA :: foo" y ejecuta la Línea B. La línea B llama al método goo () y así sucesivamente llega a la línea C. Después de ejecutar la Línea C , no queda nada para ejecutar en el constructor AA (es decir, se crea el Objeto) y, por lo tanto, el control fluye hacia el Constructor secundario (a medida que se crea el padre, es hora de que nazca el niño) y así el niño El constructor se llamaría a continuación.

Para estudiantes / principiantes, recomiendo encarecidamente pasar por Head First Java Edition. Realmente te ayuda a poner la Fundación Java Fuerte.


Se explica muy bien en los documentos oficiales:

https://docs.oracle.com/javase/tutorial/java/IandI/super.html

Si un constructor no invoca explícitamente un constructor de superclase, el compilador de Java inserta automáticamente una llamada al constructor sin argumentos de la superclase. Si la súper clase no tiene un constructor sin argumentos, obtendrá un error en tiempo de compilación. El objeto tiene tal constructor, por lo que si Objeto es la única superclase, no hay problema.

Entonces, el compilador de Java está agregando super () sin argumentos para ti.

De hecho, si la clase que está extendiendo no tiene un constructor predeterminado, tendrá que llamar a este constructor con argumentos antes.

De lo contrario, la razón por la que no se está llamando a AA:goo es porque está anulada por BB incluso si no tiene la anotación @Override , si desea ver esa llamada, necesita usar super (); en tu b: método goo. De hecho, foo no se anula porque es privado, por lo que no es posible anularlo. Si intenta agregar la anotación @Averride, tendrá un error de compilación.


También hay un defecto de diseño grave en el código de ejemplo: está llamando a un método invalidable de un constructor. Esto significa que el objeto BB puede no estar completamente inicializado en el momento en que se llama a este método. Por ejemplo:

public class AA{ public AA(){ foo(); } private void foo() { System.out.print("AA::foo "); goo(); } public void goo(){ System.out.print("AA::goo "); } } public class BB extends AA{ private Date timestamp; public BB() { super(); foo(); timestamp = new Date(); } public void foo() { System.out.print("BB:foo "); } public void goo() { // goo() gets called before timestamp is initialized // causing a NullPointerException System.out.print("BB::goo " + timestamp.getYear()); } public static void main(String[] args){ AA obj = new BB(); } }

Recuerde esto: NUNCA LLAME UN MÉTODO ANULABLE DE UN CONSTRUCTOR (ni siquiera de manera indirecta como en este ejemplo)