Un pequeño "bug" con el que me he tropezado el otro día. Supón una clase padre abstracta en la que desde el constructor se llama al método abstracto.
public abstract ClasePadre {
public ClasePadre() {
…
inicializa();
…
}public abstract void inicializa() {
}
}
Ahora imagina que hacemos una clase hija, mal hecha, tal que así
public class ClaseHija extends ClasePadre {
private UnAtributo atributo = null;@Override
public void inicializa() {
…
atributo = new UnAtributo();
…
}
}
}
Simplemente hemos sobreescrito el método inicializa() que nos obliga el padre y lo aprovechamos para inicializar un atributo que inicialmente es null. A partir de aquí, nuestro código se fia de que ese atributo esté inicializado. Pues bien, está mal. Veamos el orden de construcción cuando hacemos new ClaseHija()
- Primero java llama al constructor del padre. Este llama a inicializar() y se crea el atributo de la clase hija.
- Luego java asigna a los atributos de la clase hija los valores definidos al declararlos, o sea, pone atributo a null.
- Finalmente java llama al constructor de la clase hija.
El punto 2 es el que nos da los problemas, resulta que en el punto 1 se inicializa atributo dándole un valor y en el paso 2 se vuelve a poner a null. ¡¡ El atributo queda sin inicializar a pesar de que le hemos hecho un new !!. Nos costó un buen rato dilucidar por qué algo de lo que se hacía el new, un rato después era null.
Pues bien, esto nos ha pasado, y nos ha pasado por pasar de las métricas. Hay una que dice ConstructorCallOverridableMethod, en la que salta un error si un constructor llama a un método que no es final, es decir, que las clases hijas podrían sobreescribir y hacer que la clase padre no quedara bien inicializada. Esto no es exactamente así en este ejemplo, pero está claro que no es buena idea que un constructor llama a métodos que se pueda o, como en este ejemplo, se deban sobreescribir.