2011-07-20 12 views
6

Básicamente, lo que quiero hacer es forzar la subclase para llamar a un método abstracto de superclase (implementado en la subclase), así que no tengo que escribirlo explícitamente tiempo que creo una nueva subclase.Cómo forzar a la subclase a llamar a un método abstracto implementado

Lo escribí en el constructor de la superclase una vez, porque quiero que lo fuerce para cada implementación.

public abstract class SupahClass { 
    public SupahClass() { 
     doStuff(); // It IS executed when the subclass constructor is called 
     init(); // NOT executed, even though it's implemented 
    } 

    private void doStuff() { ... }   

    protected abstract void init(); 
} 

public class SomeSubClass extends SupahClass { 

    // The problem lies HERE: this is executed AFTER init() ... so it gets NULL again 
    private TextBox myTextBox = null; 

    public SomeSubClass() { 
     super(); // invokes the super constructor, so init() should be called 
     // I could call init(); here EACH time i create a new subclass... but no :) 
    } 

    @Override 
    public void init() { 
     this.myTextBox = new TextBox(); // Executed BEFORE its declared as null above 
    } 
} 

Por supuesto, la superclase en realidad no puede llamar desde su método de un resumen (de modo indefinido), pero es una clase abstracta por lo que no puede ser instanciado, se debe delegar la tarea a sus subclases, así que por qué ¿No pueden llamar al método abstracto pero ahora implementado?

EDITAR ver la propiedad subclase myTextBox y la aplicación init()

¿Qué enfoque le parece que debería hacer? Retire la = null en la declaración de propiedad (duhhh)

o eliminar la init() en la superclase y explícitamente llamándolo en el constructor de la subclase (esto es lo que quería evitar, ya que voy a tener que escribir el 100% de las veces ..)

+1

Eso funcionará. Evidentemente, este no es su código real; su código real debe diferir de este de alguna manera importante. ¿Por qué no publicar su código real y echarnos un vistazo? –

+0

¡De hecho!ver mi comentario sobre la respuesta de Jon Skeet y mi pregunta actualizada :) – dominicbri7

Respuesta

2

Puede solucionar este problema mediante el cambio de su declaración a:

private TextBox myTextBox; 

La asignación a nula sirve a ningún propósito útil. Si no hubiera una superclase, no haría nada, porque los campos se inicializan para anular de todos modos. Como hay una superclase, actúa como un disparo al pie. Entonces, deshazte de eso.

+0

Sí, eso es exactamente lo que hice por el momento para solucionar esto, pero me hiciste se dan cuenta de que no es útil en absoluto y de hecho es una mala práctica. Ni siquiera puedo pensar en un caso en el que sería útil ... Supongo que eliminaré estas tareas en todos los lugares donde las use. Pero aparte de eso, ¿qué piensas de mi implementación actual (de llamar a tales métodos en los constructores)? – dominicbri7

+0

Es, como todos te dirán, una práctica peligrosa. Abre la puerta a algunos errores bastante complicados (métodos que se ejecutan contra objetos parcialmente construidos, publicación de objetos inseguros, etc.). Sin embargo, no es inevitable que cause esos errores, aún depende de los programadores presentarlos. Si no había ninguna ventaja para llamar a un método de subclase de un constructor, te aconsejaría que nunca lo hicieras. Pero a menudo, es una forma simple y clara de obtener el comportamiento que desea, sin una alternativa directa. Entonces, hay una llamada de juicio sobre si el valor de la llamada supera el riesgo de errores. –

+0

Esa es una llamada de juicio que depende del contexto, de cuán complejas son las invariantes alrededor de la clase, cuán hábiles son los programadores que trabajan en la base del código, si la clase se ampliará ampliamente, o se limitará a una pequeña parte del sistema, etc. Es una pregunta específica, a la que no puedo darle una respuesta general. Jon Skeet puede, por supuesto :). –

8

No puedo reproducir esto. init()se llamará, suponiendo que no se lanzan excepciones primero. Ejemplo corto pero completo:

abstract class Superclass { 
    public Superclass() { 
     init(); 
    } 

    protected abstract void init(); 
} 

class Subclass extends Superclass { 
    public Subclass() { 
     super(); 
    } 

    @Override 
    public void init() { 
     System.out.println("Subclass.init called"); 
    } 
} 

public class Test {  
    public static void main(String[] args) throws Exception { 
     new Subclass(); 
    } 
} 

Que imprime "Subclass.init called" como se esperaba. Sospecho que algo más está mal en el código que no nos has mostrado.

Tenga en cuenta que llamar a los métodos virtuales en un constructor es una tarea riesgosa, la subclase no se habrá inicializado todavía, todas las variables tendrán sus valores predeterminados, por ejemplo. En general, es un patrón que debe evitarse.

+1

estaba a punto de sugerir esto, pero no es vergonzoso que Jon Skeet – Leon

+0

tengas razón. Pensé que Init nunca fue llamado porque en algún momento uno de las propiedades de la subclase eran nulas cuando se suponía que debían inicializarse. Tiene el doble de razón cuando dice que es un negocio arriesgado porque la subclase no se habrá inicializado: ese era exactamente mi problema. Vea mi pregunta actualizada al final para obtener más información, y me gustaría mucho su opinión sobre qué enfoque cree que debería usar ... :) – dominicbri7

2

Clases abstractas puede llamar a métodos abstractos.

0

¿Ha utilizado un depurador para entender qué está haciendo el código?

El orden de construcción es 1. constructor de clase base 2. inicializar todos los miembros de clase derivada 3. constructor de clase derivada.

En su caso, el paso 1 inicializa el miembro (en el inicio de la clase derivada - llamada mediante llamada polimórfica) pero el paso 2 lo establece en nulo.

Cuestiones relacionadas