Classes Internas Anônimas em Java
Crie implementações únicas de interfaces e classes abstratas em Java com classes internas anônimas.
Uma classe anônima é uma subclasse ou implementação de interface de uso único que você define e instancia em uma única expressão — sem nome, sem arquivo separado, sem cabeçalho de classe. Era assim que o Java lidava com callbacks, listeners e pequenos adaptadores antes da chegada das expressões lambda no Java 8. As lambdas substituíram a maioria dos seus casos de uso, mas as classes anônimas ainda são válidas, ainda são úteis em algumas situações específicas e ainda aparecem em bases de código mais antigas.
A sintaxe
A forma é new SomeType() { ... body ... }. O corpo é a definição da classe; a expressão ao redor também cria uma instância:
Runnable r = new Runnable() {
@Override
public void run() {
System.out.println("hi");
}
};
r.run();Essa única instrução (1) define uma nova classe que implementa Runnable, (2) cria uma instância dela, (3) atribui a instância a r. A classe não tem nome no seu código-fonte; o compilador gera um nome como Outer$1 no arquivo .class.
Você pode fazer o mesmo com uma classe abstrata:
Shape s = new Shape() {
@Override double area() { return 42; }
};…ou até mesmo uma classe concreta, se quiser sobrescrever um de seus métodos inline:
ArrayList<String> chatty = new ArrayList<>() {
@Override
public boolean add(String s) {
System.out.println("added " + s);
return super.add(s);
}
};Quando você usaria uma
O caso clássico: um callback cuja implementação é pequena e usada em exatamente um lugar:
button.addClickListener(new ClickListener() {
@Override
public void onClick() {
System.out.println("clicked");
}
});No Java moderno, você normalmente escreveria isso como uma lambda:
button.addClickListener(() -> System.out.println("clicked"));…e a lambda é mais curta, mais fácil de ler e não fixa uma referência à classe externa. Então quando a forma anônima ainda faz sentido?
- Múltiplos métodos. Uma lambda implementa um único método abstrato. Se você precisar sobrescrever dois métodos em uma classe abstrata ou implementar dois em uma interface, somente uma classe anônima resolve.
- Subclasse de uma classe concreta. Lambdas só visam interfaces funcionais. Para sobrescrever um método em
ArrayList,HashMapou sua própria classe concreta dinamicamente, você precisa de uma classe anônima. - Chamar
super.method(...)na sobreposição. Lambdas não têmsuper. Classes anônimas têm. - Blocos de inicialização. Classes anônimas podem ter blocos inicializadores de instância (
{ ... }); lambdas não podem.
Na prática, isso é um conjunto pequeno de casos. A maioria dos callbacks modernos usa lambdas.
Capturando variáveis
Uma classe anônima declarada dentro de um método pode ler as variáveis locais do método envolvente — mas somente se elas forem final ou efetivamente finais (nunca reatribuídas após sua inicialização):
void schedule() {
String msg = "hello";
Runnable r = new Runnable() {
@Override public void run() {
System.out.println(msg); // captures msg
}
};
r.run();
// msg = "bye"; // would make msg no longer effectively final → ERROR above
}A mesma regra se aplica às lambdas — é uma propriedade do código ao redor, não da forma sintática. O motivo é que o valor capturado é copiado para os campos da classe sintética; se msg pudesse mudar depois, a cópia capturada ficaria silenciosamente desatualizada.
Elas têm um this externo
Assim como as classes internas, classes anônimas declaradas em um contexto não estático carregam uma referência para a instância envolvente. Isso significa que this dentro da classe anônima refere-se à instância anônima, não à externa. Para acessar a instância externa, qualifique-a com Outer.this:
public class Server {
String name = "outer";
Runnable handler() {
return new Runnable() {
String name = "inner";
public void run() {
System.out.println(name); // "inner"
System.out.println(Server.this.name);// "outer"
}
};
}
}E a mesma ressalva de referência externa das classes internas se aplica: retornar uma instância anônima para código de longa duração mantém a instância externa viva.
Limitações
- Uma classe anônima pode estender uma superclasse ou implementar uma interface — não ambas, e não mais de uma de cada.
- Ela não pode ter um construtor próprio — não há nome para dar ao construtor. Você pode usar um bloco inicializador de instância (
{ ... }) para configuração. - Ela não pode ser
static.
Comparando com lambdas — a comparação em código
Mesmo trabalho, duas formas:
// Anonymous class — older style
Comparator<String> byLength = new Comparator<String>() {
@Override
public int compare(String a, String b) {
return Integer.compare(a.length(), b.length());
}
};
// Lambda — modern equivalent
Comparator<String> byLength = (a, b) -> Integer.compare(a.length(), b.length());Ambas produzem um Comparator<String>. A lambda é mais curta e tem semânticas de this/escopo ligeiramente diferentes (sem referência à classe externa para contextos de instância; this dentro de uma lambda é a instância envolvente, não um novo objeto). Se ambas as formas funcionam para o seu caso de uso, prefira a lambda.
Um exemplo trabalhado
O que vem a seguir
O tipo restante de classe aninhada é a classe local — uma classe declarada dentro do corpo de um método, com um nome real mas um escopo pequeno. Elas se sobrepõem às classes anônimas (e às lambdas), mas às vezes são a escolha mais limpa. Continue para classes locais.