CodeGym /Cursos /JAVA 25 SELF /Classes anônimas

Classes anônimas

JAVA 25 SELF
Nível 16 , Lição 2
Disponível

1. Introdução às classes anônimas

Imagine que você tem uma classe Animal que descreve como um animal se comporta. Essa classe tem um método say() que imprime "O animal emite um som". Você precisa criar um objeto que se comporte como um cachorro e imprima "Au!", mas não quer criar um arquivo separado Dog.java.

É aqui que entram as classes anônimas. São classes sem nome, declaradas e instanciadas diretamente no ponto em que são usadas. 🚀 Elas permitem herdar uma classe “na hora”, sem criar um arquivo separado. Em vez de encher o projeto com vários arquivos pequenos de classes usadas em um único lugar, você pode simplesmente escrever toda a lógica ali onde ela é necessária.

Como é a sintaxe?

A sintaxe das classes anônimas pode parecer incomum, mas na prática é bem simples:

TipPeremennoy imya = new TipDlyaNasledovaniya() {
    // Aqui escrevemos o corpo da classe anônima
    // Sobrescrevemos os métodos da classe-pai
};

Vamos destrinchar essa sintaxe:

  • TipPeremennoy imya: É uma declaração normal de variável onde vamos guardar nosso novo objeto.
  • new TipDlyaNasledovaniya(): Aqui parece que estamos criando um novo objeto. Mas, em vez do nome de uma classe, usamos o nome da classe da qual queremos herdar. Note que após os parênteses () não há ponto e vírgula !
  • { ... }: É aqui que abrimos chaves e escrevemos toda a lógica da nossa classe anônima. Podemos sobrescrever métodos da classe-pai ou adicionar nossa própria lógica.

Exemplo de herança de uma classe comum:

Vamos voltar ao nosso exemplo com o animal.

class Animal {
    void say() {
        System.out.println("O animal emite um som");
    }
}

// Criamos uma classe anônima herdando de Animal
Animal dog = new Animal() {
    // Sobrescrevemos o método say()
    @Override
    void say() {
        System.out.println("Au-au! 🐶");
    }
};

Animal cat = new Animal() {
    @Override
    void say() {
        System.out.println("Miau-miau! 🐱");
    }
};

dog.say(); // Imprimirá: Au-au! 🐶
cat.say(); // Imprimirá: Miau-miau! 🐱

Neste exemplo, criamos dois objetos, dog e cat, que são, na essência, classes anônimas herdando de Animal. E não criamos nenhum arquivo separado.

Comparação de abordagens

// Maneira comum (criamos uma classe separada)
class Dog extends Animal {
@Override
void say() { System.out.println("Au!"); }
}
Animal dog = new Dog();

// VS

// Classe anônima (tudo no mesmo lugar)
Animal dog = new Animal() {
@Override
void say() { System.out.println("Au!"); }
};

O resultado é o mesmo, mas a classe anônima é mais curta e não polui o projeto!

2. Nome da classe anônima após a compilação

Classes anônimas não têm nome no código-fonte, mas o compilador Java, claro, precisa nomeá-las de alguma forma para criar o arquivo .class. Ele faz isso por uma regra bem definida:

  • O nome do arquivo da classe anônima é composto pelo nome da classe externa em que ela foi declarada.
  • Após o nome da classe externa, adiciona-se o símbolo de cifrão $.
  • Em seguida, vem o número ordinal da classe anônima nesse arquivo, começando em 1.

Assim, se nosso exemplo com os animais estiver no arquivo Main.java, após a compilação serão criados três arquivos:

  • Main.class
  • Main$1.class (nossa classe anônima para o cachorro)
  • Main$2.class (nossa classe anônima para o gato)

Se a classe anônima for declarada dentro de um método que, por sua vez, está em uma classe interna, o nome ficará mais complexo, por exemplo, OuterClass$InnerClass$1.class.

É uma convenção interna do compilador, útil de conhecer, mas que raramente tem grande importância no dia a dia. O principal é lembrar que a classe anônima continua sendo uma classe completa, embora sem nome no código-fonte.

3. Características e limitações importantes

Classes anônimas são uma ferramenta poderosa, mas têm suas regras.

Acesso a variáveis. Uma classe anônima pode usar variáveis do método que a envolve. No entanto, essas variáveis precisam ser final ou efetivamente final (ou seja, seu valor não muda após a inicialização).

public void doSomething() {
    String greeting = "Olá!"; // Esta variável é efetivamente final

    class OuterClass {
        void greet() {
            // Criamos uma classe anônima dentro do método
            new Object() {
                void sayHello() {
                    System.out.println(greeting); // Isso é permitido
                    // greeting = "Tchau!"; // Isto causará um erro!
                }
            }.sayHello();
        }
    }

    new OuterClass().greet();
}

Por que isso? Porque a classe anônima pode “viver” mais do que o próprio método, e se ela pudesse alterar a variável, isso causaria problemas.

Sem construtor. Como a classe anônima não tem nome, ela também não pode ter construtor. Mas você pode usar um bloco de inicialização para executar código na criação do objeto:

Animal dog = new Animal() {
    // Bloco de inicialização
    {
        System.out.println("Inicialização da classe anônima 🐶");
    }
    @Override
    void say() {
        System.out.println("Au-au!");
    }
};

Restrições. Classes anônimas não podem declarar campos estáticos (exceto constantes) nem métodos estáticos. Elas sempre são criadas como parte de outro objeto, portanto não podem ser static, public, protected ou private.

4. Nuances úteis

Quando usar classes anônimas

  • Você precisa herdar uma classe (ou implementar uma interface) apenas uma vez.
  • A implementação é pequena — 1–2 métodos, não mais que algumas dezenas de linhas.
  • A classe é necessária apenas em um lugar e não faz sentido dar um nome a ela.
  • Você quer evitar “poluir” o pacote com muitas classes pequenas e descartáveis.

Cenários típicos:

  • Manipuladores de eventos (GUI, Swing, Android etc.).
  • Passagem de callbacks para métodos.
  • Implementação rápida de comparadores para ordenar coleções.
  • Objetos temporariamente alterados (por exemplo, para testes).

5. Interação com a classe externa

Se uma classe anônima for declarada dentro de um método não estático ou bloco da classe externa, ela poderá acessar os campos e métodos dessa classe externa (inclusive os privados!).

public class Outer {
    private String secret = "Texto secreto";

    // Classe base
    class Printer {
        public void print() {
            System.out.println("Saída comum");
        }
    }

    public void revealSecret() {
        Printer p = new Printer() {
            @Override
            public void print() {
                System.out.println("Acesso ao privado: " + secret);
            }
        };
        p.print();
    }

    public static void main(String[] args) {
        new Outer().revealSecret();
    }
}

6. Erros comuns ao trabalhar com classes anônimas

Erro nº 1: tentativa de alterar uma variável do método envolvente.
Se você declarou uma variável fora da classe anônima e tenta alterá-la depois de usá-la dentro da classe anônima — o compilador emitirá um erro. A variável deve ser final ou efetivamente final (não mudar após a inicialização).

Erro nº 2: classe anônima grande demais.
Se a classe anônima cresceu para dezenas de linhas e contém vários métodos — é um sinal de que vale a pena extraí-la para uma classe nomeada separada. Caso contrário, o código se tornará difícil de ler.

Erro nº 3: tentativa de usar métodos ou campos estáticos.
Em uma classe anônima não é possível declarar métodos ou campos estáticos (exceto constantes). Se isso for realmente necessário — é motivo para criar uma classe interna comum.

Erro nº 4: esquecer o escopo.
A classe anônima é visível apenas no local em que é declarada e não tem nome. Se for necessário reutilizá-la — declare uma classe comum.

Comentários
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION