Devo usar blocos inicializadores em Java?

14

Recentemente, deparei com uma construção Java que nunca vi antes e queria saber se deveria usá-la. Parece ser chamado de blocos de inicialização .

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 principais desvantagens usando essa sintaxe:

  1. É um dos poucos casos em Java onde 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 foram gravados. Isso parece prejudicial para mim, pois simplesmente alterar a ordem dos blocos de código realmente altera o código.
  2. Eu realmente não vejo nenhum benefício ao usá-lo. Na maioria dos casos, os construtores irão chamar uns aos outros com alguns valores pré-definidos. Mesmo que esse não seja o caso, o código pode simplesmente ser colocado em um método privado e chamado de cada construtor.
  3. Isso reduz a legibilidade, já que você poderia 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 forem verdadeiras, por que (e quando) essa introdução de linguagem foi introduzida? Há algum caso de uso legítimo?

    
por dirkk 12.05.2014 / 16:50
fonte

5 respostas

20

Existem dois casos em que eu uso blocos inicializadores.

O primeiro é para inicializar os membros finais. Em Java, você pode inicializar um membro final em linha com a declaração ou inicializá-lo no construtor. Em um método, é proibido atribuir a um membro final.

Isso é válido:

final int val = 2;

Isso também é válido:

final int val;

MyClass() {
    val = 2;
}

Isso é inválido:

final int val;

MyClass() {
    init();
}

void init() {
    val = 2;  // cannot assign to 'final' field in a method
}

Se você tiver vários construtores e não puder inicializar um membro final em linha (porque a lógica de inicialização é muito complexa) ou se os construtores não puderem chamar a si mesmos, você poderá copiar / colar o código de inicialização ou você pode usar um bloco inicializador.

final int val;
final int squareVal;

MyClass(int v, String s) {
    this.val = v;
    this.s = s;
}

MyClass(Point p, long id) {
    this.val = p.x;
    this.id = id;
}

{
    squareVal = val * val;
}

O outro caso de uso que tenho para blocos inicializadores é para construir estruturas de dados auxiliares pequenas. Eu declaro um membro e coloco valores nele logo após suas declarações em seu próprio bloco inicializador.

private Map<String, String> days = new HashMap<String, String>();
{
    days.put("mon", "monday");
    days.put("tue", "tuesday");
    days.put("wed", "wednesday");
    days.put("thu", "thursday");
    days.put("fri", "friday");
    days.put("sat", "saturday");
    days.put("sun", "sunday");
}
    
por 12.05.2014 / 17:27
fonte
10

Em geral, não use blocos inicializadores não-estáticos (e talvez evite os estáticos também).

Sintaxe confusa

Olhando para esta questão, existem 3 respostas, mas você enganou 4 pessoas com esta sintaxe. Eu era um deles e venho escrevendo 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:

public class Test {
    private String something;

    // Default constructor does some things
    public Test() { doStuff(); }

    // Other constructors call the default constructor
    public Test(String s) {
        this(); // Call default constructor
        something = s;
    }
}

Padrão do Construtor

Se você precisar doStuff () 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 ideia. Os construtores demoram um pouco para escrever, mas devidamente escritos, eles são uma alegria para usar.

public class Test {
    // Value can be final (immutable)
    private final String something;

    // Private constructor.
    private Test(String s) { something = s; }

    // Static method to get a builder
    public static Builder builder() { return new Builder(); }

    // builder class accumulates values until a valid Test object can be created. 
    private static class Builder {
        private String tempSomething;
        public Builder something(String s) {
            tempSomething = s;
            return this;
        }
        // This is our factory method for a Test class.
        public Test build() {
            Test t = new Test(tempSomething);
            // Here we do your extra initialization after the
            // Test class has been created.
            doStuff();
            // Return a valid, potentially immutable Test object.
            return t;
        }
    }
}

// Now you can call:
Test t = Test.builder()
             .setString("Utini!")
             .build();

Loops do Inicializador estático

Eu costumava usar muitos estáticos inicializadores, mas ocasionalmente corri para loops onde 2 classes dependiam dos blocos de inicializadores estáticos uns dos outros sendo chamados antes que a classe pudesse ser totalmente carregada. Isso produziu uma "falha ao carregar classe" ou mensagem de erro similarmente vaga. Eu tive que comparar arquivos com a última versão de trabalho conhecida no controle de origem para descobrir qual era o problema. Não tem graça nenhuma.

Inicialização Preguiçosa

Talvez os inicializadores estáticos sejam bons por motivos de desempenho quando funcionam e não são muito confusos. Mas, em geral, prefiro inicialização preguiçosa para inicializadores estáticos nos dias de hoje. Está claro o que eles fazem, ainda não corri com 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 construir estruturas de dados, (compare com exemplos nas outras respostas), eu agora uso Funções auxiliares de definição de dados imutáveis do Paguro :

private ImMap<String,String> days =
        map(tup("mon", "monday"),
            tup("tue", "tuesday"),
            tup("wed", "wednesday"),
            tup("thu", "thursday"),
            tup("fri", "friday"),
            tup("sat", "saturday"),
            tup("sun", "sunday"));

Conculsion

No início de 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 inicializadores, caso você os veja no código legado, ou eles aparecem em um teste, mas se eu estivesse fazendo uma revisão de código e vi um em um novo código, peço que justifique por que nenhum deles alternativas acima eram adequadas antes de dar o seu código o polegar para cima.

    
por 24.06.2016 / 21:15
fonte
3

Além da inicialização de uma variável de instância declarada como final (consulte resposta do barjak ) , Eu também mencionaria o bloco de inicialização static .

Você pode usá-los como um tipo de "contructor 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 de barjak:

public class dayHelper(){
    private static Map<String, String> days = new HashMap<String, String>();
    static {
        days.put("mon", "monday");
        days.put("tue", "tuesday");
        days.put("wed", "wednesday");
        days.put("thu", "thursday");
        days.put("fri", "friday");
        days.put("sat", "saturday");
        days.put("sun", "sunday");
    }
    public static String getLongName(String shortName){
         return days.get(shortName);
    }
}
    
por 12.05.2014 / 17:44
fonte
1

Como fas como blocos inicializadores não estáticos, sua função básica é atuar como um construtor padrão em classes anônimas. Isso é basicamente seu único direito de existir.

    
por 24.06.2016 / 20:28
fonte
0

Eu concordo totalmente com as afirmações 1, 2, 3. Eu também nunca uso inicializadores de bloco por estas razões e eu não sei porque existe em Java.

No entanto, sou forçado a usar o inicializador de blocos estático em um caso: quando tenho que instanciar um campo estático cujo construtor pode gerar uma exceção verificada.

private static final JAXBContext context = JAXBContext.newInstance(Foo.class); //doesn't compile

Mas, em vez disso, você precisa fazer isso:

private static JAXBContext context;
static {
    try
    {
        context = JAXBContext.newInstance(Foo.class);
    }
    catch (JAXBException e)
    {
        //seriously...
    }
}

Acho esse idioma muito feio (também evita que você marque context as final ), mas essa é a única maneira suportada pelo Java para inicializar esses campos.

    
por 25.06.2016 / 09:08
fonte

Tags