Sobes.tech
Junior

Em que casos pode aplicar o modificador final em Java?

sobes.tech IA

Resposta da IA

O modificador final em Java pode ser aplicado a:

  1. Variáveis:

    • Variáveis locais: O seu valor é atribuído uma vez e não pode ser alterado após a inicialização.
    • Campos de classe: O valor do campo é atribuído na declaração ou no construtor e depois não pode ser alterado.
    • Parâmetros de método: O parâmetro dentro do método torna-se uma constante; não pode ser reatribuído dentro do corpo do método.

    Aplicar final às variáveis garante:

    • Imutabilidade: Garantia de que o valor não mudará.
    • Segurança em multithreading: Os campos final de uma classe são visíveis a todos os threads após a conclusão do construtor sem sincronização adicional.
    • Otimizações do compilador: O compilador pode realizar algumas otimizações sabendo que o valor não mudará.
  2. Métodos:

    • Um método declarado como final não pode ser sobrescrito em subclasses.
    • Isto é útil para:
      • Prevenir alterações indesejadas de comportamento: Garante que a lógica do método permaneça inalterada.
      • Fomentar a composição: Incentiva o uso de composição em vez de herança.
      • Possíveis otimizações do compilador: O compilador pode fazer inline das chamadas a métodos final.
  3. Classes:

    • Uma classe declarada como final não pode ter subclasses.
    • O uso de final numa classe é para:
      • Criar classes imutáveis: Por exemplo, classes envolventes de tipos primitivos (Integer, String).
      • Proteger o design: Impede a extensão que pode violar os invariantes da classe.
      • Aumentar a segurança: Garante que o comportamento da classe não possa ser alterado através da criação de subclasses.
Aplicação Descrição Vantagens
final campo O valor é atribuído uma vez (na declaração ou no construtor). Imutabilidade, segurança em multithreading, otimizações.
final parâmetro O valor não pode ser alterado dentro do método. Assinatura clara, garantia de imutabilidade do valor do parâmetro.
final método Não pode ser sobrescrito em subclasses. Prevenir alterações de comportamento, fomentar a composição, otimizações.
final classe Não pode ter subclasses (não é possível herdar). Imutabilidade da classe, proteção do design, aumento de segurança.

Exemplos:

public class Example {
    // campo final de classe
    private final String name;

    // campo primitivo final
    private final int id;

    // campo final, mas pode ser referência a diferentes objetos List enquanto a lista não for criada
    // após a criação, pode-se alterar a referência, mas não a referência em si
    private final List<String> items;

    // Constante de tempo de compilação
    public static final double PI = 3.14159;

    public Example(String name, int id) {
        this.name = name; // atribuição ao campo final
        this.id = id;     // atribuição ao campo final
        this.items = new ArrayList<>(); // inicialização do campo final
    }

    // método final
    public final void printDetails() {
        System.out.println("Name: " + name + ", ID: " + id);
    }

    // método com parâmetro final
    public void addItem(final String item) {
        // item = "novo elemento"; // erro de compilação: não é possível atribuir a uma variável final
        items.add(item);
    }
}

// classe final
// public final class ImmutableClass {
//     private final int value;
//
//     public ImmutableClass(int value) {
//         this.value = value;
//     }
//
//     public int getValue() {
//         return value;
//     }
// }

// public class SubclassOfImmutable extends ImmutableClass { // erro de compilação: não é possível herdar de uma classe final
// }