visual una studio sirve ser resueltos qué que puede programacion polimorfismo para overwriting overrride overrider orientada objetos metodos metodo llamar hijo herencia form especificada ejercicios ejemplos ejemplo distinta definicion codigo clases clase c# java c++ inheritance polymorphism

c# - una - que es overriding java



Llamar a un método anulado desde un ctor de clase padre (2)

Intenté llamar a un método anulado de un constructor de una clase padre y noté un comportamiento diferente en los idiomas.

C++ - hace eco de A.foo()

class A{ public: A(){foo();} virtual void foo(){cout<<"A.foo()";} }; class B : public A{ public: B(){} void foo(){cout<<"B.foo()";} }; int main(){ B *b = new B(); }

Java - hace eco de B.foo()

class A{ public A(){foo();} public void foo(){System.out.println("A.foo()");} } class B extends A{ public void foo(){System.out.println("B.foo()");} } class Demo{ public static void main(String args[]){ B b = new B(); } }

C# - hace eco de B.foo()

class A{ public A(){foo();} public virtual void foo(){Console.WriteLine("A.foo()");} } class B : A{ public override void foo(){Console.WriteLine("B.foo()");} } class MainClass { public static void Main (string[] args) { B b = new B(); } }

Me doy cuenta de que, en C ++, los objetos se crean a partir de la parte superior de la jerarquía principal, por lo que cuando el constructor llama al método anulado, B ni siquiera existe, por lo que llama a la versión A ''del método. Sin embargo, no estoy seguro de por qué tengo un comportamiento diferente en Java y C # (de C ++)


En C ++, como anotó correctamente, el objeto es de tipo A hasta que finaliza el constructor A El objeto realmente cambia de tipo durante su construcción. Esta es la razón por la que se utiliza vtable de la clase A , por lo que se llama a A::foo() lugar de B::foo() .

En Java y C #, se usa todo el vtable (o mecanismo equivalente) del tipo más derivado, incluso durante la construcción de las clases base. Así que en estos idiomas, se llama a B.foo() .

Tenga en cuenta que generalmente no se recomienda llamar a un método virtual desde el constructor. Si no eres muy cuidadoso, el método virtual podría suponer que el objeto está completamente construido, aunque no sea así. En Java, donde todos los métodos son implícitamente virtuales, no tienes otra opción.


Si bien entiendo que está haciendo esto para experimentos, es importante tener en cuenta la siguiente cita de Effective Java 2nd Edition, Ítem 17: Diseñar y documentar para herencia, o bien prohibirlo :

Hay algunas restricciones más que una clase debe obedecer para permitir la herencia. Los constructores no deben invocar métodos reemplazables , directa o indirectamente. Si viola esta regla, se producirá un fallo del programa. El constructor de la superclase se ejecuta antes que el constructor de la subclase, por lo que se invocará el método de reemplazo de la subclase antes de que se haya ejecutado el constructor de la subclase. Si el método de anulación depende de cualquier inicialización realizada por el constructor de la subclase, el método no se comportará como se espera.

Aquí hay un ejemplo para ilustrar:

public class ConstructorCallsOverride { public static void main(String[] args) { abstract class Base { Base() { overrideMe(); } abstract void overrideMe(); } class Child extends Base { final int x; Child(int x) { this.x = x; } @Override void overrideMe() { System.out.println(x); } } new Child(42); // prints "0" } }

Aquí, cuando el constructor de la Base llama a overrideMe , el Child no ha terminado de inicializar el final int x , y el método obtiene el valor incorrecto. Esto casi seguramente dará lugar a errores y errores.