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 concretosm
(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 deC
m
espublic
,protected
o declarado con acceso al paquete en el mismo paquete queC
- Ningún método declarado en
C
tiene una firma que es una firma de firma dem
.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 desdeT
un método declarado en un supertipo deT
, o si no es equivalente a un método público deObject
, entonces se produce un error en tiempo de compilación .
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 invocationsuper();
, 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)