java inheritance constructor initialization constructor-chaining

java - Inicializar campo antes de que se ejecute super constructor?



super java (5)

En Java, ¿hay alguna manera de inicializar un campo antes de que se ejecute el super constructor?

Incluso los hacks más feos que se me ocurren son rechazados por el compilador:

class Base { Base(String someParameter) { System.out.println(this); } } class Derived extends Base { private final int a; Derived(String someParameter) { super(hack(someParameter, a = getValueFromDataBase())); } private static String hack(String returnValue, int ignored) { return returnValue; } public String toString() { return "a has value " + a; } }

Nota: El problema desapareció cuando cambié de herencia a delegación, pero aún me gustaría saberlo.


Como han dicho otros, no puede inicializar el campo de instancia antes de llamar al constructor de la superclase.

Pero hay soluciones. Una es crear una clase de fábrica que obtenga el valor y lo pase al constructor de la clase Derivada.

class DerivedFactory { Derived makeDerived( String someParameter ) { int a = getValueFromDataBase(); return new Derived( someParameter, a ); } } class Derived extends Base { private final int a; Derived(String someParameter, int a0 ) { super(hack(someParameter, a0)); a = a0; } ... }


El constructor Súper se ejecutará en cualquier caso, pero ya que estamos hablando de los "hacks más feos", podemos aprovechar esto

public class Base { public Base() { init(); } public Base(String s) { } public void init() { //this is the ugly part that will be overriden } } class Derived extends Base{ @Override public void init(){ a = getValueFromDataBase(); } }

Nunca sugiero usar este tipo de hacks.


Está prohibido por la especificación del lenguaje Java (sección 8.8.7) :

La primera declaración de un cuerpo constructor puede ser una invocación explícita de otro constructor de la misma clase o de la superclase directa.

El cuerpo del constructor debería verse así:

ConstructorBody:

{ ExplicitConstructorInvocationopt BlockStatementsopt }


No, no hay forma de hacer esto.

De acuerdo con las especificaciones del lenguaje , las variables de instancia ni siquiera se inicializan hasta que se realiza una llamada a super() .

Estos son los pasos realizados durante el paso de constructor de creación de instancia de clase, tomados del enlace:

  1. Asigne los argumentos para el constructor a las variables de parámetros recién creados para esta invocación de constructor.
  2. Si este constructor comienza con una invocación de constructor explícita (§8.8.7.1) de otro constructor en la misma clase (usando esto), entonces evalúe los argumentos y procese esa invocación de constructor recursivamente usando estos mismos cinco pasos. Si la invocación del constructor finaliza abruptamente, entonces este procedimiento se completa abruptamente por la misma razón; de lo contrario, continúe con el paso 5.
  3. Este constructor no comienza con una invocación de constructor explícita de otro constructor en la misma clase (usando esto). Si este constructor es para una clase que no sea Object, entonces este constructor comenzará con una invocación explícita o implícita de un constructor de superclase (usando super). Evalúe los argumentos y procese esa invocación de constructor de superclase recursivamente utilizando estos mismos cinco pasos. Si la invocación del constructor finaliza abruptamente, entonces este procedimiento se completa abruptamente por el mismo motivo. De lo contrario, continúe con el paso 4.
  4. Ejecute los inicializadores de instancia y los inicializadores de variable de instancia para esta clase, asignando los valores de los inicializadores de variable de instancia a las variables de instancia correspondientes, en el orden de izquierda a derecha en el que aparecen textualmente en el código fuente de la clase. Si la ejecución de cualquiera de estos inicializadores da como resultado una excepción, no se procesan más inicializadores y este procedimiento se completa abruptamente con la misma excepción. De lo contrario, continúe con el paso 5.
  5. Ejecuta el resto del cuerpo de este constructor. Si la ejecución se completa abruptamente, entonces este procedimiento se completa abruptamente por el mismo motivo. De lo contrario, este procedimiento se completa normalmente.

Tengo una forma de hacer esto.

class Derived extends Base { private final int a; // make this method private private Derived(String someParameter, int tmpVar /*add an addtional parameter*/) { // use it as a temprorary variable super(hack(someParameter, tmpVar = getValueFromDataBase())); // assign it to field a a = tmpVar; } // show user a clean constructor Derived(String someParameter) { this(someParameter, 0) } ... }