W3docs

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, HashMap ou sua própria classe concreta dinamicamente, você precisa de uma classe anônima.
  • Chamar super.method(...) na sobreposição. Lambdas não têm super. 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

java— editable, runs on the server

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.

Prática

Prática
No Java moderno, qual tarefa ainda requer uma classe anônima em vez de uma lambda?
No Java moderno, qual tarefa ainda requer uma classe anônima em vez de uma lambda?
Was this page helpful?