Item 83: Utilize a inicialização preguiçosa com parcimônia

O que é inicialização preguiçosa? Definição: Adiar a inicialização de um campo até que ele seja acessado pela primeira vez. Benefícios: Evita inicializações desnecessárias se o campo nunca for usado. Aplicações: Usada para campos estáticos e de instância. Práticas Recomendadas e Exemplos Inicialização Normal (Preferível) Simples e eficaz. Exemplo: private final FieldType field = computeFieldValue(); Use inicialização normal para a maioria dos campos, salvo necessidade especial. Inicialização Preguiçosa com Getter Sincronizado Quando usar: Para resolver circularidades de inicialização. Exemplo: private FieldType field; synchronized FieldType getField() { if (field == null) { field = computeFieldValue(); } return field; } 3. Prática da Classe Portadora (Para Campos Estáticos) Quando usar: Inicialização preguiçosa eficiente para campos estáticos. Exemplo: private static class FieldHolder { static final FieldType field = computeFieldValue(); } static FieldType getField() { return FieldHolder.field; } Vantagem: Inicializa a classe apenas quando o campo é acessado, com custo mínimo após a inicialização. 4. Prática de Verificação Dupla (Para Campos de Instância) Quando usar: Para desempenho em inicialização preguiçosa em campos de instância. Exemplo: private volatile FieldType field; FieldType getField() { FieldType result = field; if (result == null) { // Primeira verificação (sem bloqueio) synchronized (this) { result = field; if (result == null) { // Segunda verificação (com bloqueio) field = result = computeFieldValue(); } } } return result; } 5. Prática de Verificação Única (Inicialização Repetida Permitida) Quando usar: Campos que podem tolerar inicializações repetidas. Exemplo private volatile FieldType field; FieldType getField() { if (field == null) { // Verificação única field = computeFieldValue(); } return field; } 6. Prática Ousada de Verificação Única Quando usar: Apenas se tolerar inicializações extras e se o tipo do campo for um primitivo diferente de long ou double. Exemplo: private FieldType field; FieldType getField() { if (field == null) { // Sem volatile field = computeFieldValue(); } return field; } Considerações Gerais Trade-offs: Inicialização preguiçosa minimiza o custo inicial, mas pode aumentar o custo de acesso ao campo. Avalie com medições de desempenho. Sincronização em Multithread: Fundamental para evitar bugs graves. Utilize práticas seguras (e.g., volatile, bloqueios). Uso Preferencial: Campos Estáticos: Prática da classe portadora. Campos de Instância: Verificação dupla. Inicialização Repetida Permitida: Verificação única. Resumo Final Inicialize normalmente sempre que possível. Use inicialização preguiçosa apenas quando necessário para desempenho ou para resolver problemas de circularidade

Jan 9, 2025 - 06:47
 0
Item 83: Utilize a inicialização preguiçosa com parcimônia

O que é inicialização preguiçosa?

  • Definição: Adiar a inicialização de um campo até que ele seja acessado pela primeira vez.
  • Benefícios: Evita inicializações desnecessárias se o campo nunca for usado.
  • Aplicações: Usada para campos estáticos e de instância.

Práticas Recomendadas e Exemplos

  1. Inicialização Normal (Preferível) Simples e eficaz.

Exemplo:

private final FieldType field = computeFieldValue();

Use inicialização normal para a maioria dos campos, salvo necessidade especial.

  1. Inicialização Preguiçosa com Getter Sincronizado Quando usar: Para resolver circularidades de inicialização.

Exemplo:

private FieldType field;

synchronized FieldType getField() {
    if (field == null) {
        field = computeFieldValue();
    }
    return field;
}

3. Prática da Classe Portadora (Para Campos Estáticos)

  • Quando usar: Inicialização preguiçosa eficiente para campos estáticos.

Exemplo:

private static class FieldHolder {
    static final FieldType field = computeFieldValue();
}

static FieldType getField() {
    return FieldHolder.field;
}

Vantagem: Inicializa a classe apenas quando o campo é acessado, com custo mínimo após a inicialização.

4. Prática de Verificação Dupla (Para Campos de Instância)

  • Quando usar: Para desempenho em inicialização preguiçosa em campos de instância.

Exemplo:

private volatile FieldType field;

FieldType getField() {
    FieldType result = field;
    if (result == null) { // Primeira verificação (sem bloqueio)
        synchronized (this) {
            result = field;
            if (result == null) { // Segunda verificação (com bloqueio)
                field = result = computeFieldValue();
            }
        }
    }
    return result;
}

5. Prática de Verificação Única (Inicialização Repetida Permitida)

  • Quando usar: Campos que podem tolerar inicializações repetidas.

Exemplo

private volatile FieldType field;

FieldType getField() {
    if (field == null) { // Verificação única
        field = computeFieldValue();
    }
    return field;
}

6. Prática Ousada de Verificação Única

  • Quando usar: Apenas se tolerar inicializações extras e se o tipo do campo for um primitivo diferente de long ou double.

Exemplo:

private FieldType field;

FieldType getField() {
    if (field == null) { // Sem volatile
        field = computeFieldValue();
    }
    return field;
}

Considerações Gerais

Trade-offs:

  • Inicialização preguiçosa minimiza o custo inicial, mas pode aumentar o custo de acesso ao campo.
  • Avalie com medições de desempenho.

Sincronização em Multithread:

  • Fundamental para evitar bugs graves.
  • Utilize práticas seguras (e.g., volatile, bloqueios).

Uso Preferencial:

  • Campos Estáticos: Prática da classe portadora.
  • Campos de Instância: Verificação dupla.
  • Inicialização Repetida Permitida: Verificação única.

Resumo Final

  • Inicialize normalmente sempre que possível.
  • Use inicialização preguiçosa apenas quando necessário para desempenho ou para resolver problemas de circularidade