¿Debo crear instancias de variables de instancia en la declaración o en el constructor?


¿Hay alguna ventaja para cualquiera de los dos enfoques?

Ejemplo 1:

class A {
    B b = new B();
}

Ejemplo 2:

class A {
    B b;

    A() {
         b = new B();
    }
}
Author: Jeroen Vannevel, 2010-01-03

13 answers

  • No hay diferencia - la inicialización de la variable de instancia es realmente puesta en el constructor(s) por el compilador.
  • La primera variante es más legible.
  • No puede tener manejo de excepciones con la primera variante.
  • Existe adicionalmente el bloque de inicialización, que también es puesto en el constructor(s) por el compilador:

    {
        a = new A();
    }
    

Verifique La explicación y el consejo de Sun

De este tutorial :

Las declaraciones de campo, sin embargo, no son parte de ningún método, por lo que no pueden ejecutarse como lo son las declaraciones. En su lugar, el compilador Java genera código de inicialización de campos de instancia automáticamente y lo coloca en el constructor o constructores de la clase. El código de inicialización se inserta en un constructor en el orden en que aparece en el código fuente, lo que significa que un inicializador de campos puede usar los valores iniciales de los campos declarados antes se.

Además, es posible que desee inicializar perezosamente su campo. En los casos en que la inicialización de un campo es una operación costosa, puede inicializarlo tan pronto como sea necesario:

ExpensiveObject o;

public ExpensiveObject getExpensiveObject() {
    if (o == null) {
        o = new ExpensiveObject();
    }
    return o;
}

Y en última instancia (como señala Bill), por el bien de la gestión de dependencias, es mejor evitar usar el operador new en cualquier lugar dentro de su clase. En su lugar, usar Inyección de dependencia es preferible, es decir, dejar que alguien más (otro class / framework) instanciar e inyectar las dependencias en su clase.

 246
Author: Bozho,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-09-09 14:54:27

Otra opción sería usar Inyección de dependencias.

class A{
   B b;

   A(B b) {
      this.b = b;
   }
}

Esto elimina la responsabilidad de crear el objeto B del constructor de A. Esto hará que su código sea más comprobable y más fácil de mantener a largo plazo. La idea es reducir el acoplamiento entre las dos clases A y B. Un beneficio que esto le da es que ahora puede pasar cualquier objeto que extienda B (o implemente B si es una interfaz) al constructor de A y funcionará. Una desventaja es que renuncias a la encapsulación del objeto B, por lo que está expuesto al llamador del constructor A. Tendrá que considerar si los beneficios valen esta compensación, pero en muchos casos lo son.

 34
Author: Bill the Lizard,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-02-09 13:55:38

Hoy me quemé de una manera interesante:

class MyClass extends FooClass {
    String a = null;

    public MyClass() {
        super();     // Superclass calls init();
    }

    @Override
    protected void init() {
        super.init();
        if (something)
            a = getStringYadaYada();
    }
}

Ver el error? Resulta que el inicializador a = null se llama después de se llama al constructor de superclase. Dado que el constructor de superclase llama a init(), la inicialización de a es seguida de por la inicialización de a = null.

 14
Author: Edward Falk,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-05-20 00:08:57

Mi "regla" personal (casi nunca rota) es:

  • declare todas las variables al inicio de un bloque
  • hacer que todas las variables sean definitivas a menos que no puede ser
  • declare una variable por línea
  • nunca inicializar una variable donde declarado
  • solo inicializar algo en un constructor cuando necesita datos de el constructor para hacer el inicialización

Así que tendría un código como:

public class X
{
    public static final int USED_AS_A_CASE_LABEL = 1; // only exception - the compiler makes me
    private static final int A;
    private final int b;
    private int c;

    static 
    { 
        A = 42; 
    }

    {
        b = 7;
    }

    public X(final int val)
    {
        c = val;
    }

    public void foo(final boolean f)
    {
        final int d;
        final int e;

        d = 7;

        // I will eat my own eyes before using ?: - personal taste.
        if(f)
        {
            e = 1;
        }
        else
        {
            e = 2;
        }
    }
}

De esta manera siempre estoy 100% seguro de dónde buscar declaraciones de variables (al comienzo de un bloque), y sus asignaciones (tan pronto como tenga sentido después de la declaración). Esto termina siendo potencialmente más eficiente, ya que nunca inicializa una variable con un valor que no se utiliza (por ejemplo, declarar e iniciar vars y luego lanzar una excepción antes de la mitad de los vars necesarios para tener un valor). Tampoco termina haciendo inicialización sin sentido (como int i = 0; y luego más tarde, antes de que se use "i", do i = 5;.

I valora mucho la consistencia, así que seguir esta "regla" es algo que hago todo el tiempo, y hace que sea mucho más fácil trabajar con el código ya que no tienes que buscar para encontrar cosas.

Su kilometraje puede variar.

 14
Author: TofuBeer,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-06-23 18:34:21

El ejemplo 2 es menos flexible. Si agrega otro constructor, debe recordar crear instancias del campo en ese constructor también. Solo instanciar el campo directamente, o introducir la carga perezosa en algún lugar de un getter.

Si la instanciación requiere algo más que un simple new, use un bloque inicializador. Esto se ejecutará independientemente de del constructor utilizado. Por ejemplo,

public class A {
    private Properties properties;

    {
        try {
            properties = new Properties();
            properties.load(Thread.currentThread().getContextClassLoader().getResourceAsStream("file.properties"));
        } catch (IOException e) {
            throw new ConfigurationException("Failed to load properties file.", e); // It's a subclass of RuntimeException.
        }
    }

    // ...

}
 7
Author: BalusC,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-02-18 15:03:26

Supongo que es casi solo una cuestión de gusto, siempre y cuando la inicialización sea simple y no necesite ninguna lógica.

El enfoque constructor es un poco más frágil si no utiliza un bloque inicializador, porque si más tarde agrega un segundo constructor y olvida inicializar b allí, obtendrá un null b solo cuando use ese último constructor.

Véase http://java.sun.com/docs/books/tutorial/java/javaOO/initial.html para más detalles sobre la inicialización en Java (y para explicaciones sobre bloques initalizer y otras características de inicialización no conocidas).

 4
Author: Vinko Vrsalovic,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-01-03 07:31:47

Siempre es preferible usar inyección de dependencia o inicialización perezosa, como ya se explicó a fondo en otras respuestas.

Cuando no quieres o no puedes usar esos patrones, y para tipos de datos primitivos, hay tres razones convincentes que puedo pensar de por qué es preferible inicializar los atributos de la clase fuera del constructor:

  1. se evitó la repetición = si tienes más de un constructor, o cuando necesitarás agregue más, no tendrá que repetir la inicialización una y otra vez en todos los cuerpos de los constructores;
  2. legibilidad mejorada = puede saber fácilmente con un vistazo qué variables tendrán que inicializarse desde fuera de la clase;
  3. líneas de código reducidas = por cada inicialización realizada en la declaración habrá una línea menos en el constructor.
 3
Author: Marco Lackovic,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-02-27 18:33:22

Ambos métodos son aceptables. Tenga en cuenta que en este último caso b=new B() puede no ser inicializado si hay otro constructor presente. Piense en el código del inicializador fuera del constructor como un constructor común y el código se ejecuta.

 1
Author: Chandra Patni,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-01-03 07:00:38

Creo que el ejemplo 2 es preferible. Creo que la mejor práctica es declarar fuera del constructor e inicializar en el constructor.

 1
Author: jkeesh,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-01-03 07:01:26

El segundo es un ejemplo de inicialización perezosa. El primero es la inicialización más simple, son esencialmente iguales.

 0
Author: fastcodejava,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-01-03 07:01:51

Hay una razón más sutil para inicializar fuera del constructor que nadie ha mencionado antes (muy específica debo decir). Si está utilizando herramientas UML para generar diagramas de clases a partir del código (ingeniería inversa), creo que la mayoría de las herramientas notarán la inicialización del Ejemplo 1 y lo transferirán a un diagrama (si prefiere que muestre los valores iniciales, como yo). No tomarán estos valores iniciales del ejemplo 2. Una vez más, esta es una razón muy específica-si usted están trabajando con herramientas UML, pero una vez que aprendí eso, estoy tratando de tomar todos mis valores predeterminados fuera de constructor a menos que, como se mencionó antes, haya un problema de posible lanzamiento de excepciones o lógica complicada.

 0
Author: tulu,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-06-09 14:15:52

La segunda opción es preferible ya que permite utilizar diferentes lógicas en los ctors para la instanciación de clases y utilizar el encadenamiento de los ctors. Por ejemplo,

class A {
    int b;

    // secondary ctor
    A(String b) {
         this(Integer.valueOf(b));
    }

    // primary ctor
    A(int b) {
         this.b = b;
    }
}

Así que la segunda opción es más flexible.

 0
Author: Andriy Kryvtsun,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-06-14 14:10:28

No he visto lo siguiente en las respuestas:

Una posible ventaja de tener la inicialización en el momento de la declaración podría ser con IDE de hoy en día donde se puede saltar muy fácilmente a la declaración de una variable (en su mayoría Ctrl-<hover_over_the_variable>-<left_mouse_click>) desde cualquier lugar de su código. A continuación, se ve inmediatamente el valor de esa variable. De lo contrario, debe "buscar" el lugar donde se realiza la inicialización (principalmente: constructor).

Esta ventaja es, por supuesto, secundaria a todos los demás razonamientos lógicos, pero para algunas personas que "característica" podría ser más importante.

 0
Author: GeertVc,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-07-30 09:13:27