¿En qué se diferencia un inicializador de instancia de un constructor?

81

En otras palabras, ¿por qué necesitaría un inicializador de instancia? ¿Qué diferencia o ventaja tiene al escribir un inicializador de instancia sobre un constructor?

Ajay
fuente
2
Los inicializadores de instancia son bastante raros (a menos que se encuentre con el código de alguien interesado en el idioma de doble llave).
Tom Hawtin - tackline

Respuestas:

108

Esto parece explicarlo bien:

Los inicializadores de instancia son una alternativa útil a los inicializadores de variables de instancia siempre que:

  • el código inicializador debe detectar excepciones, o

  • realizar cálculos sofisticados que no se pueden expresar con un inicializador de variable de instancia. Por supuesto, siempre podría escribir ese código en los constructores.

Pero en una clase que tuviera varios constructores, tendría que repetir el código en cada constructor. Con un inicializador de instancia, puede escribir el código una sola vez, y se ejecutará sin importar qué constructor se use para crear el objeto. Los inicializadores de instancia también son útiles en clases internas anónimas, que no pueden declarar ningún constructor en absoluto.

Desde: Inicialización del objeto JavaWorld en Java .

javamonkey79
fuente
17
Por otro lado, puede escribir código una vez en un constructor y simplemente llamarlo desde todos los demás constructores. Pero las clases internas anónimas tienen un buen punto.
Tadeusz Kopec
11
Puede llamarlo desde otros constructores, pero luego vuelve a repetir la llamada. Si agrega un nuevo constructor, debe recordar agregarle la llamada. No es así con un inicializador de instancia.
talonx
5
@talonx, estoy de acuerdo con su argumento sobre el olvido, pero utilizar el comportamiento predeterminado es igualmente peligroso. Cuando un seguidor está leyendo un constructor en código heredado, uno no siempre recordaría buscar posibles inicializadores de instancia. Mientras que un init () utilizado explícitamente se destacará.
Assambar
1
@ javamonkey79: ¿Estás diciendo que si elijo constructor en lugar de inicializador de instancia para mi clase, el único lugar donde el inicializador de instancia es útil es cuando trabajo con clases anónimas?
realPK
4
@Assambar No puede asignar campos finales en su método init (), pero puede hacerlo en un bloque inicializador.
Timmos
22

En términos de ciclo de vida de los objetos, no hay diferencia. Ambos se invocan en el momento de la construcción y, lógicamente, el bloque inicializador puede considerarse parte de la construcción.

Semánticamente, un inicializador es una buena herramienta por varias razones:

el inicializador puede mejorar la legibilidad del código manteniendo la lógica de inicialización junto a la variable que se está inicializando:

   public class Universe {
       public int theAnswer;
       {
         int SIX = 6;
         int NINE = 7;
         theAnswer = SIX * NINE;
       }

       // a bunch of other vars
   }

vs

   public class Universe {
       public int theAnswer;

       // a bunch of other vars

       public Universe() {
         int SIX = 6;
         int NINE = 7;
         theAnswer = SIX * NINE;

         // other constructor logic
       }
   }

Los inicializadores se invocan independientemente del constructor que se utilice.

Los inicializadores se pueden usar en clases internas anónimas, donde los constructores no pueden.

ykaganovich
fuente
3
Lo que tiene es técnicamente un "inicializador de variable de instancia", no un "inicializador de instancia" (un bloque directamente anidado dentro de una clase). Ver JLS 3rd sección 8.6.
Tom Hawtin - tackline
El bloque inicializador de instancias no tiene un nombre como se muestra aquí . ¿No es así? Ha marcado el código de inicialización de su instancia con un nombre theAnswer. ¿Es eso correcto? o me falta algo.
RBT
1
@RBT theAnsweres una variable de instancia declarada. Se inicializa en un bloque inicializador anónimo. Tenga en cuenta el punto y coma después de la declaración de variable.
ykaganovich
9

Cuando tiene muchos constructores y desea que se ejecute un código común para cada constructor, puede usar el inicializador de instancia, ya que se llama para todos los constructores.

Rahul Garg
fuente
4

Evitaría el idioma del inicializador de instancias en general: la única ventaja real que ofrece sobre los inicializadores de variables es el manejo de excepciones.

Y dado que un método init (invocable desde el constructor) también puede realizar el manejo de excepciones y también centraliza el código de configuración del constructor, pero tiene la ventaja de que puede operar en los valores de los parámetros del constructor, yo diría que el inicializador de la instancia es redundante y, por lo tanto, debe ser evitado.

CPerkins
fuente
1
Tendría que llamar manualmente al método init en TODOS sus constructores.
Stephan
2
@Alex Buen punto, pero dado que la mayoría de las clases con varios constructores tienen una lógica común, tienden a llamarse entre sí de todos modos. Al menos en la mayoría de mis clases.
CPerkins
3

La ventaja real de los inicializadores de instancia sobre los constructores se ve cuando usamos una clase interna anónima .

Las clases internas anónimas no pueden tener un constructor (ya que son anónimas) , por lo que son un ajuste bastante natural, por ejemplo, los inicializadores .

padippist
fuente
1

En el momento de la creación del objeto, si queremos realizar la inicialización de las variables de instancia, entonces deberíamos optar por Constructor, además de la actividad de inicialización si queremos realizar alguna actividad en el momento de la creación del objeto, entonces deberíamos ir por el bloque de instancia.

No podemos reemplazar el constructor con el bloque de instancia porque el constructor puede tomar argumentos pero el bloque de instancia no puede tomar argumentos.

No podemos reemplazar el bloque de instancia con el constructor porque una clase puede contener más de un constructor. Si queremos reemplazar el bloque de instancia con el constructor, entonces en cada constructor tenemos que escribir código de bloque de instancia porque en tiempo de ejecución no podemos esperar qué constructor se llamará, esto aumentará innecesariamente el código duplicado.

Ejemplo:

class MyClass{

    static int object_count = 0;

    MyClass(){
        object_count++;
    }

    MyClass(int i){

        object_count++;
    }

    void getCount() {

        System.out.println(object_count);
    }

    public static void main(String... args) {
        MyClass one = new MyClass();
        MyClass two = new MyClass(2);
        two.getCount();
    }
}

Salida: 2

class MyClass{

    static int object_count = 0;

    {
        object_count++;
    }

    MyClass(){

    }

    MyClass(int i){     

    }

    void getCount() {

        System.out.println(object_count);
    }

    public static void main(String... args) {
        MyClass one = new MyClass();
        MyClass two = new MyClass(2);
        two.getCount();
    }
}

Salida: 2

NPE
fuente
0

Initializer es una forma de compartir código entre constructores y hace que el código sea más legible si se usa inicializador con declaración de variable.

El compilador de Java copia los bloques inicializadores en cada constructor. Por lo tanto, este enfoque se puede utilizar para compartir un bloque de código entre varios constructores. Documentación de Oracle

dnyanesh
fuente