Pergunta

Então eu fui escovar acima em minhas habilidades Java como de tarde e de ter encontrado alguns pedaços de funcionalidade que eu não sabia sobre anteriormente. Estáticos e de instância Initializers são duas dessas técnicas.

A minha pergunta é quando seria um usar um inicializador em vez de incluir o código em um construtor? Pensei em algumas possibilidades óbvias:

  • estático / instância initializers pode ser usado para definir o valor das variáveis ??"finais" static / instância enquanto que um construtor não pode

  • inicializadores estáticos pode ser usado para definir o valor de quaisquer variáveis ??estáticas em uma classe, que deve ser mais eficiente do que ter um bloco "se (someStaticVar == null) // fazer coisas" de código no início de cada construtor

Ambos os casos assumem que o código necessário para definir essas variáveis ??é mais complexa do que simplesmente "var = valor", caso contrário não haveria parece haver qualquer razão para usar um inicializador em vez de simplesmente definir o valor ao declarar a variável.

No entanto, enquanto estes não são ganhos triviais (especialmente a capacidade de definir uma variável final), parece que há um número bastante limitado de situações em que um inicializador deve ser usado.

Pode-se certamente usar um inicializador para muito do que é feito em um construtor, mas eu realmente não vejo a razão para fazê-lo. Mesmo que todos os construtores para uma classe compartilham uma grande quantidade de código, o uso de um initialize privada () função parece fazer mais sentido para mim do que usar um inicializador porque não trancá-lo em ter que executar código quando se escreve um novo construtor.

Estou faltando alguma coisa? Há uma série de outras situações em que um inicializador deve ser usado? Ou é realmente apenas uma ferramenta bastante limitado para ser usado em situações muito específicas?

Foi útil?

Solução

initializers

estáticos são úteis como cletus mencionado e eu usá-los da mesma maneira. Se você tiver uma variável estática que deve ser inicializado quando a classe é carregada, em seguida, uma estática initializer é o caminho a percorrer, especialmente uma vez que permite que você faça uma inicialização complexa e ainda tem a variável estática ser final. Esta é uma grande vitória.

I encontrar "se (someStaticVar == null) // fazer coisas" para ser confuso e propenso a erros. Se ele é inicializado estaticamente e declarou final, então você evitar a possibilidade de ele ser null.

No entanto, estou confuso quando você diz:

estático / instância initializers pode ser usado para definir o valor da "final" static / variáveis ??de instância, ao passo que um construtor não pode

Eu suponho que você está dizendo ambos:

  • inicializadores estáticos pode ser usado para definir o valor de variáveis ??estáticas "finais" enquanto que um construtor não pode
  • instância initializers pode ser usado para definir o valor da instância de variáveis ??"finais" enquanto que um construtor não pode

e você está correto sobre o primeiro ponto, errado no segundo. Você pode, por exemplo, faça o seguinte:

class MyClass {
    private final int counter;
    public MyClass(final int counter) {
        this.counter = counter;
    }
}

Além disso, quando um monte de código é compartilhado entre construtores, uma das melhores maneiras de lidar com isso é para construtores de cadeia, fornecendo os valores padrão. Isso faz com que seja muito claro o que está sendo feito:

class MyClass {
    private final int counter;
    public MyClass() {
        this(0);
    }
    public MyClass(final int counter) {
        this.counter = counter;
    }
}

Outras dicas

anônimos classes internas não pode ter um construtor (como eles são anônimos), por isso eles são um ajuste muito natural por exemplo initializers.

eu mais frequentemente usam estática initializer blocos para a criação de dados estáticos finais, especialmente coleções. Por exemplo:

public class Deck {
  private final static List<String> SUITS;

  static {
    List<String> list = new ArrayList<String>();
    list.add("Clubs");
    list.add("Spades");
    list.add("Hearts");
    list.add("Diamonds");
    SUITS = Collections.unmodifiableList(list);
  }

  ...
}

Agora este exemplo pode ser feito com uma única linha de código:

private final static List<String> SUITS =
  Collections.unmodifiableList(
    Arrays.asList("Clubs", "Spades", "Hearts", "Diamonds")
  );

mas a versão estática pode ser muito mais puro, particularmente quando os itens são não-trivial para inicializar.

A implementação ingênua também não pode criar uma lista de unmodifiable, o que é um erro potencial. O acima cria uma estrutura de dados imutáveis ??que você pode facilmente retornar de métodos públicos e assim por diante.

Apenas para adicionar a alguns já excelentes pontos aqui. O inicializador estático é thread-safe. executou-se quando a classe é carregada, e, assim, torna-se mais simples de inicialização de dados estáticos do que usar um construtor, em que você precisaria de um bloco sincronizado para verificar se os dados estáticos é inicializado e, em seguida, na verdade, inicialize-o.

public class MyClass {

    static private Properties propTable;

    static
    {
        try 
        {
            propTable.load(new FileInputStream("/data/user.prop"));
        } 
        catch (Exception e) 
        {
            propTable.put("user", System.getProperty("user"));
            propTable.put("password", System.getProperty("password"));
        }
    }

contra

public class MyClass 
{
    public MyClass()
    {
        synchronized (MyClass.class) 
        {
            if (propTable == null)
            {
                try 
                {
                    propTable.load(new FileInputStream("/data/user.prop"));
                } 
                catch (Exception e) 
                {
                    propTable.put("user", System.getProperty("user"));
                    propTable.put("password", System.getProperty("password"));
                }
            }
        }
    }

Não se esqueça, agora você tem que sincronizar a classe, não nível de instância. Isto implica um custo para cada exemplo construído em vez de um custo uma vez que quando a classe é carregada. Além disso, é feio; -)

Eu li um artigo inteiro à procura de uma resposta para a ordem de inicialização de initializers contra seus construtores. Eu não encontrá-lo, então eu escrevi algum código para verificar o meu entendimento. Eu pensei que eu gostaria de acrescentar esta pequena demonstração como um comentário. Para testar sua compreensão, veja se você pode prever a resposta antes de lê-lo na parte inferior.

/**
 * Demonstrate order of initialization in Java.
 * @author Daniel S. Wilkerson
 */
public class CtorOrder {
  public static void main(String[] args) {
    B a = new B();
  }
}

class A {
  A() {
    System.out.println("A ctor");
  }
}

class B extends A {

  int x = initX();

  int initX() {
    System.out.println("B initX");
    return 1;
  }

  B() {
    super();
    System.out.println("B ctor");
  }

}

Output:

java CtorOrder
A ctor
B initX
B ctor

A estática initializer é o equivalente a um construtor no contexto estático. Você certamente vai ver que mais frequentemente do que um inicializador de instância. Às vezes você precisa executar o código para configurar o ambiente estático.

Em geral, uma instância initalizer é melhor para classes internas anônimas. Dê uma olhada na de JMock livro de receitas para ver uma forma inovadora de usá-lo para tornar o código mais legível.

Às vezes, se você tem alguma lógica que é complicado de cadeia através construtores (digamos que você está subclasse e você não pode chamar este () porque você precisa chamar super ()), você poderia evitar a duplicação, fazendo o material comum no exemplo initalizer. initalizers instância são tão raros, porém, que eles são uma sintaxe surpreendente para muitos, então eu evitá-los e preferem fazer minha classe concreta e não anónimo se eu precisar o comportamento construtor.

JMock é uma exceção, porque é assim que o quadro se destina a ser utilizado.

Há um aspecto importante que você tem que considerar em sua escolha:

blocos de inicialização são membros da classe / objeto, enquanto construtores não são . Isso é importante quando se considera extensão / subclasse :

  1. Initializers são herdadas por subclasses. (Embora, pode ser sombreada)
    Isto significa que é basicamente garantiu que subclasses são inicializados como pretendido pela classe pai.
  2. Construtores são não herdou , no entanto. (Eles só chamar super() [ou seja, sem parâmetros] implícita ou você tem que fazer uma chamada específica super(...) manualmente.)
    Isto significa que é possível que uma chamada implícita ou exclicit super(...) pode não inicializar a subclasse como pretendido pela classe pai.

Veja este exemplo de um bloco inicializador:

class ParentWithInitializer {
    protected final String aFieldToInitialize;

    {
        aFieldToInitialize = "init";
        System.out.println("initializing in initializer block of: " 
            + this.getClass().getSimpleName());
    }
}

class ChildOfParentWithInitializer extends ParentWithInitializer{
    public static void main(String... args){
        System.out.println(new ChildOfParentWithInitializer().aFieldToInitialize);
    }
}

saída:
initializing in initializer block of: ChildOfParentWithInitializer init
-.> Não importa o que os construtores os implementos da subclasse, o campo será inicializado

Agora, considere este exemplo com construtores:

class ParentWithConstructor {
    protected final String aFieldToInitialize;

    // different constructors initialize the value differently:
    ParentWithConstructor(){
        //init a null object
        aFieldToInitialize = null;
        System.out.println("Constructor of " 
            + this.getClass().getSimpleName() + " inits to null");
    }

    ParentWithConstructor(String... params) {
        //init all fields to intended values
        aFieldToInitialize = "intended init Value";
        System.out.println("initializing in parameterized constructor of:" 
            + this.getClass().getSimpleName());
    }
}

class ChildOfParentWithConstructor extends ParentWithConstructor{
    public static void main (String... args){
        System.out.println(new ChildOfParentWithConstructor().aFieldToInitialize);
    }
}

saída:
Constructor of ChildOfParentWithConstructor inits to null null
-.> Isto irá inicializar o campo para null por padrão, embora possa não ser o resultado que queria

Eu também gostaria de acrescentar um ponto ao longo de todo o exposto respostas fabulosas. Quando carregar um driver de JDBC usando Class.forName ( "") o carregamento de classe acontece e a estática inicializador da classe driver é demitido e o código dentro registra driver para driver Manager. Este é um dos o uso significativo de bloco de código estático.

Como você mencionou, não é útil em muitos casos, e como acontece com qualquer sintaxe menos usado, você provavelmente vai querer evitá-lo apenas para parar a próxima pessoa a olhar para o seu código de gastar os 30 segundos para retirá-lo de abóbadas.

Por outro lado, é a única maneira de fazer algumas coisas (eu acho que você praticamente coberto aqueles).

As variáveis ??estáticas si deve ser um pouco evitado de qualquer maneira -. Nem sempre, mas se você usar um monte deles, ou você usar um monte em uma classe, você pode encontrar diferentes abordagens, o seu próprio futuro será obrigado

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top