Devo instanciar variables ​​de instância na declaração ou no construtor?

Existe alguma vantagem para qualquer abordagem?

Exemplo 1:

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

Exemplo 2:

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

  • Não há diferença – a boot da variável de instância é realmente colocada no (s) construtor (es) pelo compilador.
  • A primeira variante é mais legível.
  • Você não pode ter tratamento de exceção com a primeira variante.
  • Existe adicionalmente o bloco de boot, que também é colocado no (s) construtor (es) pelo compilador:

     { a = new A(); } 

Verifique a explicação e o conselho da Sun

Deste tutorial :

As declarações de campo, no entanto, não fazem parte de nenhum método, portanto, não podem ser executadas como as instruções são. Em vez disso, o compilador Java gera código de boot de campo de instância automaticamente e o coloca no construtor ou construtores da class. O código de boot é inserido em um construtor na ordem em que aparece no código-fonte, o que significa que um inicializador de campo pode usar os valores iniciais dos campos declarados antes dele.

Além disso, você pode querer inicializar o seu campo com lentidão . Nos casos em que inicializar um campo é uma operação cara, você pode inicializá-lo assim que for necessário:

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

E, no final das contas (como apontado por Bill), para o gerenciamento de dependencies, é melhor evitar o uso do new operador em qualquer lugar dentro de sua class. Em vez disso, é preferível usar Injeção de Dependência – isto é, permitir que outra pessoa (outra class / framework) instancie e injete as dependencies de sua class.

Outra opção seria usar a Injeção de Dependência .

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

Isso elimina a responsabilidade de criar o object B do construtor de A Isso tornará seu código mais testável e mais fácil de manter a longo prazo. A ideia é reduzir o acoplamento entre as duas classs A e B Um benefício que isso lhe dá é que agora você pode passar qualquer object que estenda B (ou implemente B se for uma interface) para o construtor de A e ele funcionará. Uma desvantagem é que você desiste do encapsulamento do object B , portanto ele é exposto ao chamador do construtor A Você terá que considerar se os benefícios valem a pena, mas em muitos casos eles são.

Eu me queimei de uma maneira interessante hoje:

 class MyClass extends FooClass { String a = null; public MyClass() { super(); // Superclass calls init(); } @Override protected void init() { super.init(); if (something) a = getStringYadaYada(); } } 

Veja o erro? Acontece que o inicializador a = null é chamado depois que o construtor da superclass é chamado. Como o construtor da superclass chama init (), a boot de a é seguida pela boot a = null .

minha “regra” pessoal (quase sempre quebrada) é:

  • declarar todas as variables ​​no início de um bloco
  • tornar todas as variables ​​finais, a menos que elas não possam ser
  • declarar uma variável por linha
  • nunca inicializar uma variável quando declarada
  • apenas inicializar algo em um construtor quando precisar de dados do construtor para fazer a boot

Então eu teria 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; } } } 

Dessa forma, tenho sempre a certeza de onde procurar as declarações de variables ​​(no início de um bloco) e suas atribuições (assim que fizer sentido após a declaração). Isso acaba sendo potencialmente mais eficiente, já que você nunca inicializa uma variável com um valor que não é usado (por exemplo, declare e init vars e, em seguida, lança uma exceção antes que metade dessas variables ​​precise ter um valor). Você também não acaba fazendo uma boot sem sentido (como int i = 0; e depois, antes de “i” ser usado, do = 5 ;.

Eu valorizo ​​muito a consistência, então seguir essa “regra” é algo que eu faço o tempo todo, e isso torna muito mais fácil trabalhar com o código, já que você não precisa caçar para encontrar coisas.

Sua milhagem pode variar.

O exemplo 2 é menos flexível. Se você adicionar outro construtor, precisará lembrar de instanciar o campo nesse construtor também. Apenas instancie o campo diretamente ou introduza um carregamento lento em algum lugar em um getter.

Se a instanciação requer mais do que apenas um new simples, use um bloco inicializador. Isso será executado independentemente do construtor usado. Por exemplo

 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. } } // ... } 

Acho que é quase apenas uma questão de gosto, desde que a boot seja simples e não precise de lógica.

A abordagem do construtor é um pouco mais frágil se você não usar um bloco inicializador, porque se você adicionar mais tarde um segundo construtor e esquecer de inicializá-lo, você obterá um nulo b apenas quando usar o último construtor.

Consulte http://java.sun.com/docs/books/tutorial/java/javaOO/initial.html para obter mais detalhes sobre a boot em Java (e para explicações sobre os blocos do initalizer e outros resources de boot pouco conhecidos).

Usando injeção de dependência ou boot lenta é sempre preferível, como já foi explicado em outras respostas.

Quando você não quer ou não pode usar esses padrões, e para tipos de dados primitivos, há três razões convincentes que posso pensar em por que é preferível inicializar os atributos de class fora do construtor:

  1. evitou repetição = se você tiver mais de um construtor, ou quando precisar adicionar mais, você não precisará repetir a boot repetidamente em todos os corpos dos construtores;
  2. melhor legibilidade = você pode facilmente dizer rapidamente quais variables ​​terão que ser inicializadas de fora da class;
  3. linhas reduzidas de código = para cada boot feita na declaração, haverá uma linha a menos no construtor.

Ambos os methods são aceitáveis. Note que no último caso b=new B() pode não ser inicializado se houver outro construtor presente. Pense no código inicializador fora do construtor como um construtor comum e o código é executado.

Eu acho que o Exemplo 2 é preferível. Eu acho que a melhor prática é declarar fora do construtor e inicializar no construtor.

O segundo é um exemplo de boot lenta. Primeiro é uma boot mais simples, eles são essencialmente os mesmos.

Há mais uma razão sutil para inicializar fora do construtor que ninguém mencionou antes (muito específico, devo dizer). Se você estiver usando ferramentas UML para gerar diagramas de classs a partir do código (engenharia reversa), a maioria das ferramentas que eu acredito notará a boot do Exemplo 1 e as transferirá para um diagrama (se você preferir mostrar os valores iniciais, como Eu faço). Eles não obterão esses valores iniciais do Exemplo 2. Novamente, esse é um motivo muito específico – se você estiver trabalhando com ferramentas UML, mas quando souber disso, tento obter todos os meus valores padrão fora do construtor, a menos que, como estava mencionado anteriormente, há uma questão de possível lançamento excepcional ou lógica complicada.

A segunda opção é preferível, pois permite usar diferentes lógicas em instâncias para instanciação de classs e usar o encadeamento de capacitores. Por exemplo

 class A { int b; // secondary ctor A(String b) { this(Integer.valueOf(b)); } // primary ctor A(int b) { this.b = b; } } 

Então a segunda opção é mais flexível.

Eu não vi o seguinte nas respostas:

Uma possível vantagem de ter a boot no momento da declaração pode ser nos dias de hoje, onde é possível pular facilmente para a declaração de uma variável (principalmente Ctrl-- ) de qualquer lugar em seu código. Você então imediatamente vê o valor dessa variável. Caso contrário, você terá que “procurar” o local em que a boot foi feita (principalmente: construtor).

Essa vantagem é naturalmente secundária a todos os outros raciocínios lógicos, mas para algumas pessoas esse “recurso” pode ser mais importante.