Classes Locais em Java
Declare classes dentro de corpos de métodos em Java com classes internas locais, com acesso a variáveis locais efetivamente finais.
Uma classe local é uma classe declarada dentro do corpo de um método (ou de um construtor, ou de um bloco inicializador). Seu escopo se limita ao bloco em que foi declarada — nada fora desse bloco pode referenciá-la pelo nome. Fora isso, ela se comporta como uma classe interna, tendo acesso aos membros da instância envolvente e às variáveis locais efetivamente finais do método.
Classes locais são raras no Java moderno. Lambdas substituíram seus principais casos de uso para callbacks de um único método, e classes anônimas costumam ser mais concisas para implementações pontuais. Mas uma classe local é, ocasionalmente, a opção mais limpa quando você precisa de um tipo nomeado que participa da lógica do método envolvente e não tem utilidade em nenhum outro lugar.
Se você ainda não conhece os outros membros da família de classes aninhadas, os capítulos sobre a visão geral de classes aninhadas e classes internas definem os termos usados abaixo.
Declarando uma
Uma declaração de classe dentro de um corpo de método:
public void process(List<String> lines) {
class Counter {
int n = 0;
void inc() { n++; }
}
Counter c = new Counter();
for (String s : lines) if (!s.isBlank()) c.inc();
System.out.println("non-blank lines: " + c.n);
}Fora de process, ninguém pode ver Counter — seu nome não existe além da chave de fechamento do método. Por dentro, você a usa como qualquer outra classe.
Por que escolhê-la em vez de uma classe anônima?
Uma classe local tem um nome real, o que lhe confere algumas vantagens que uma classe anônima não tem:
- Um construtor. Você pode fornecer parâmetros, escrever validações e ter múltiplos construtores.
- Múltiplas instâncias. Como a classe tem um nome, você pode escrever
new Counter()mais de uma vez. Uma definição de classe anônima cria apenas uma instância por expressão. - Autoreferência.
Counterpode receber a si mesma como parâmetro, manter um campoCounter next, recorrer — tudo o que tipos nomeados podem fazer. - Legibilidade. Um nome como
Counterdocumenta para que serve o auxiliar. Blocos anônimosnew Object() { ... }são mais difíceis de percorrer visualmente.
Acesso ao estado envolvente
Classes locais podem:
- Ler e escrever campos da instância envolvente (se o método envolvente for não estático).
- Chamar métodos de instância da classe envolvente.
- Ler — mas não reatribuir — as variáveis locais e parâmetros finais ou efetivamente finais do método envolvente.
void run(int multiplier) {
class Scaler {
int scale(int x) { return x * multiplier; } // captures the parameter
}
System.out.println(new Scaler().scale(7));
}multiplier é capturado na classe sintética como um campo. O requisito de "efetivamente final" é a razão disso — se multiplier pudesse ser reatribuído dentro de run após Scaler ter capturado seu valor antigo, os dois discordariam silenciosamente.
Em um método estático
Em um método static, a classe local é implicitamente estática — ela não tem instância envolvente para referenciar:
public static int demo() {
class Helper {
int square(int n) { return n * n; }
}
return new Helper().square(7);
}Não há Outer.this aqui, porque o método envolvente é estático. Helper ainda pode capturar variáveis locais efetivamente finais de demo, mas não campos de instância de um objeto envolvente que não existe.
Quando realmente usá-la
Casos concretos em que uma classe local supera genuinamente as alternativas:
- O auxiliar tem mais de um método, e você prefere ver os métodos agrupados com um nome claro em vez de espalhados pelo boilerplate de classes anônimas.
- O auxiliar precisa de um construtor para validar sua configuração.
- O auxiliar é recursivo ou mantém referências a outras instâncias de seu próprio tipo.
Se um lambda ou uma classe anônima curta servir, prefira isso. Classes locais são uma ferramenta de nicho.
Modificadores
Uma classe local aceita apenas um conjunto restrito de modificadores:
final— marca a classe como não extensível. Ela já não tem nome para código externo estendê-la, então isso é principalmente uma declaração de intenção.abstract— legal, mas raro: seria necessária uma segunda classe local no mesmo bloco para estendê-la.- Modificadores de acesso (
public,protected,private) não são permitidos. Uma classe local existe inteiramente dentro de um bloco, então não há escopo fora desse bloco a partir do qual a visibilidade pudesse ser controlada.
Ela também não pode ser declarada static diretamente. Se ela captura ou não uma instância envolvente depende de o método ao redor ser estático, conforme mostrado acima — não de uma palavra-chave.
Um exemplo prático
O que vem a seguir
Isso conclui o tour pelas classes aninhadas — e a maior parte do mecanismo clássico de POO do Java. A próxima seção da Parte 6 entra nos tipos especiais de classes: tipos que o Java fornece para formatos específicos de dados, começando com enum para conjuntos fixos de constantes. Continue para Enums em Java.