Recentemente, deparei com uma construção Java que nunca tinha visto antes e estava me perguntando se deveria usá-la. Parece ser chamado de blocos inicializadores .
public class Test {
public Test() { /* first constructor */ }
public Test(String s) { /* second constructor */ }
// Non-static initializer block - copied into every constructor:
{
doStuff();
}
}
O bloco de código será copiado em cada construtor, ou seja, se você tiver vários construtores, não precisará reescrever o código.
No entanto, vejo três desvantagens principais usando esta sintaxe:
- É um dos poucos casos em Java em que a ordem do seu código é importante, pois você pode definir vários blocos de código e eles serão executados na ordem em que são gravados. Isso me parece prejudicial, pois simplesmente alterar a ordem dos blocos de código realmente mudará o código.
- Eu realmente não vejo nenhum benefício ao usá-lo. Na maioria dos casos, os construtores se chamam com alguns valores predefinidos. Mesmo que não seja esse o caso, o código pode ser simplesmente colocado em um método privado e chamado de cada construtor.
- Isso reduz a legibilidade, pois você pode colocar o bloco no final da classe e o construtor normalmente está no início da classe. É bastante contra-intuitivo olhar para uma parte completamente diferente de um arquivo de código, se você não espera que isso seja necessário.
Se minhas afirmações acima são verdadeiras, por que (e quando) essa construção de linguagem foi introduzida? Existem casos de uso legítimos?
{ doStuff(); }
nível de classe é um bloco de inicialização.doStuff()
Respostas:
Existem dois casos em que eu uso blocos inicializadores.
O primeiro é para inicializar os membros finais. Em Java, você pode inicializar um membro final ou alinhado com a declaração ou inicializá-lo no construtor. Em um método, é proibido atribuir a um membro final.
Isso é válido:
Isso também é válido:
Isto é inválido:
Se você tiver vários construtores e se não puder inicializar um membro final embutido (porque a lógica de inicialização é muito complexa) ou se os construtores não puderem se chamar, copie / cole o código de inicialização ou use um bloco inicializador.
O outro caso de uso que eu tenho para blocos inicializadores é a construção de pequenas estruturas de dados auxiliares. Declaro um membro e coloco valores logo após suas declarações em seu próprio bloco inicializador.
fonte
squareVal = val * val
, reclamam sobre o acesso a valores não inicializados. Blocos inicializadores não podem depender de nenhum argumento passado ao construtor. A solução usual que eu já vi para esse tipo de problema é definir um único construtor de "base" com a lógica complexa e definir todos os outros construtores em termos desse. De fato, a maioria dos usos de inicializadores de instância pode ser substituída por esse padrão.Em geral, não use blocos inicializadores não estáticos (e talvez evite também estáticos).
Sintaxe Confusa
Olhando para esta pergunta, existem 3 respostas, mas você enganou 4 pessoas com esta sintaxe. Eu era um deles e escrevo Java há 16 anos! Claramente, a sintaxe é potencialmente propensa a erros! Eu ficaria longe disso.
Construtores telescópicos
Para coisas realmente simples, você pode usar construtores "telescópicos" para evitar essa confusão:
Padrão do Construtor
Se você precisar fazerStuff () no final de cada construtor ou outra inicialização sofisticada, talvez um padrão de construtor seja o melhor. Josh Bloch lista várias razões pelas quais os construtores são uma boa idéia. Os construtores demoram um pouco para escrever, mas adequadamente escritos, eles são uma alegria de usar.
Loops de Inicializador Estático
Eu costumava usar muito inicializadores estáticos , mas ocasionalmente encontrava loops em que duas classes dependiam dos blocos de inicializadores estáticos um do outro serem chamados antes que a classe pudesse ser totalmente carregada. Isso produziu uma mensagem de erro "falha ao carregar a classe" ou uma vaga igualmente semelhante. Eu tive que comparar arquivos com a última versão de trabalho conhecida no controle de origem para descobrir qual era o problema. Nada divertido.
Inicialização lenta
Talvez os inicializadores estáticos sejam bons por razões de desempenho quando funcionam e não são muito confusos. Mas, em geral, estou preferindo a inicialização lenta a inicializadores estáticos atualmente. Está claro o que eles fazem, ainda não encontrei um bug de carregamento de classe e eles funcionam em mais situações de inicialização do que os blocos de inicialização.
Definição de dados
Em vez de inicialização estática para a construção de estruturas de dados (compare com os exemplos nas outras respostas), agora uso as funções auxiliares de definição de dados imutáveis do Paguro :
Conculsion
No início do Java, os blocos inicializadores eram a única maneira de fazer algumas coisas, mas agora eles são confusos, propensos a erros e, na maioria dos casos, foram substituídos por alternativas melhores (detalhadas acima). É interessante saber sobre os blocos de inicializadores, caso você os veja em código legado ou eles surjam em um teste, mas se eu estivesse revisando o código e vi um no novo código, pediria que justificasse por que nenhum dos as alternativas acima foram adequadas antes de dar o polegar para cima ao código.
fonte
Além da inicialização de uma variável de instância declarada como
final
(consulte a resposta do barjak ), eu também mencionaria ostatic
bloco de inicialização.Você pode usá-los como uma espécie de "contratante estático".
Dessa forma, você pode fazer inicializações complexas em uma variável estática na primeira vez em que a classe é referenciada.
Aqui está um exemplo inspirado no exemplo de barjak:
fonte
No que diz respeito aos blocos inicializadores não estáticos, sua função básica é atuar como um construtor padrão em classes anônimas. Esse é basicamente o único direito deles de existir.
fonte
Eu concordo totalmente com as declarações 1, 2, 3. Também nunca uso inicializadores de bloco por esses motivos e não sei por que ele existe em Java.
No entanto, sou forçado a usar o inicializador de bloco estático em um caso: quando preciso instanciar um campo estático cujo construtor pode gerar uma exceção verificada.
Mas, em vez disso, você deve fazer:
Acho esse idioma muito feio (também impede que você marque
context
comofinal
), mas é a única maneira suportada pelo Java para inicializar esses campos.fonte
context = null;
seu bloco de captura, poderá declarar o contexto como final.The final field context may already have been assigned
static { JAXBContext tempCtx = null; try { tempCtx = JAXBContext.newInstance(Foo.class); } catch (JAXBException ignored) { ; } context = tempCtx; }