CodeGym /Cursos /JAVA 25 SELF /Classes anônimas: diferença de lambda, exemplos

Classes anônimas: diferença de lambda, exemplos

JAVA 25 SELF
Nível 48 , Lição 4
Disponível

1. Aprofundando em classes anônimas

Uma classe anônima é uma subclasse sem nome ou implementação de interface criada diretamente no ponto de uso. Antes do surgimento das lambdas (Java 8), era a maneira mais conveniente de implementar “de passagem” uma interface ou classe abstrata.

Clássico do gênero:

Runnable r = new Runnable() {
    @Override
    public void run() {
        System.out.println("Olá da classe anônima!");
    }
};
r.run();

Aqui declaramos e, ao mesmo tempo, implementamos a interface Runnable — sem arquivo separado e sem nome de classe. Essas implementações eram usadas com frequência para manipuladores de eventos, comparadores, threads e outras tarefas em que é preciso “injetar” um comportamento rapidamente.

Se a lambda é uma “expressão sob demanda”, a classe anônima é um “pequeno ator sem nome” — faz um papel breve e desaparece.

2. Comparação com expressões lambda

Sintaxe

Classe anônima:

Comparator<String> comp = new Comparator<String>() {
    @Override
    public int compare(String a, String b) {
        return a.length() - b.length();
    }
};

Expressão lambda:

Comparator<String> comp = (a, b) -> a.length() - b.length();

A diferença é evidente: a lambda é mais compacta — não é necessário declarar os tipos explicitamente, o nome do método e chaves extras quando a ação é simples.

Funcionalidade

  • Classe anônima — um objeto completo. É possível declarar campos, métodos adicionais, sobrescrever métodos de Object (toString, equals etc.).
  • Expressão lambda — implementação de um único método abstrato de uma interface funcional. Dentro dela não é possível declarar campos próprios ou métodos adicionais.

Quando escolher o quê?

  • Lambda — quando for necessário implementar rapidamente um único método de uma interface funcional.
  • Classe anônima — quando for necessário:
    • implementar vários métodos (por exemplo, de uma classe abstrata);
    • declarar campos para estado;
    • sobrescrever métodos de Object (por exemplo, toString);
    • usar particularidades de herança/acesso (por exemplo, a membros protegidos da superclasse).

3. Escopo e a palavra‑chave this

Aqui há uma armadilha comum:

  • na classe anônima, this se refere à instância da própria classe anônima;
  • na lambda, this se refere à classe externa onde a lambda foi declarada.

Exemplo: comparando o comportamento

public class Outer {
    String name = "Classe externa";

    void test() {
        Runnable anon = new Runnable() {
            String name = "Classe anônima";
            @Override
            public void run() {
                System.out.println(this.name); // "Classe anônima"
            }
        };
        Runnable lambda = () -> System.out.println(this.name); // "Classe externa"

        anon.run();
        lambda.run();
    }
}

Saída:

Classe anônima
Classe externa

Em uma classe anônima, this aponta para a própria classe anônima (pega seu campo name). Em uma lambda, this é Outer.

4. Quando usar classes anônimas?

Se for necessário implementar mais de um método

Lambda funciona apenas com interfaces funcionais (exatamente um método abstrato). Se a interface/classe abstrata exige implementar vários métodos — use uma classe anônima.

abstract class Animal {
    abstract void say();
    abstract void jump();
}

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

Se for preciso armazenar estado (campos)

Runnable r = new Runnable() {
    int counter = 0;
    @Override
    public void run() {
        counter++;
        System.out.println("Chamado " + counter + " vez(es)");
    }
};
r.run(); // Chamado 1 vez(es)
r.run(); // Chamado 2 vez(es)

Se for necessário sobrescrever métodos de Object

Comparator<String> comp = new Comparator<String>() {
    @Override
    public int compare(String a, String b) {
        return a.length() - b.length();
    }
    @Override
    public String toString() {
        return "Comparador por comprimento da string";
    }
};
System.out.println(comp); // Comparador por comprimento da string

5. Exemplos: Comparator e Runnable — lambda vs classe anônima

Ordenação de strings por comprimento

Classe anônima:

List<String> words = Arrays.asList("boi", "urso", "lobo", "tigre");
words.sort(new Comparator<String>() {
    @Override
    public int compare(String a, String b) {
        return a.length() - b.length();
    }
});
System.out.println(words);

Expressão lambda:

List<String> words = Arrays.asList("boi", "urso", "lobo", "tigre");
words.sort((a, b) -> a.length() - b.length());
System.out.println(words);

O resultado é o mesmo, mas o código com lambda é mais curto e mais legível.

Runnable: iniciar uma thread

Classe anônima:

Thread t1 = new Thread(new Runnable() {
    @Override
    public void run() {
        System.out.println("Thread via classe anônima");
    }
});
t1.start();

Expressão lambda:

Thread t2 = new Thread(() -> System.out.println("Thread via lambda"));
t2.start();

Classe anônima com campos

Runnable r = new Runnable() {
    int count = 0;
    @Override
    public void run() {
        count++;
        System.out.println("Chamado " + count + " vez(es)");
    }
};
r.run(); // Chamado 1 vez(es)
r.run(); // Chamado 2 vez(es)

Em lambda isso não é possível — não há como declarar um campo.

6. Particularidades: escopo, variáveis e final

Tanto em classes anônimas quanto em lambdas, variáveis locais do método externo podem ser usadas apenas se forem final ou “efetivamente final” (não mudam após a inicialização). Mas há um detalhe com nomes:

  • na classe anônima é possível declarar uma variável com o mesmo nome da externa (“sombreamento”);
  • em uma lambda — não: o nome não pode conflitar com o nome da variável externa.

Exemplo:

int x = 10;
Runnable r = new Runnable() {
    @Override
    public void run() {
        int x = 20; // OK: sombreia a variável externa
        System.out.println(x); // 20
    }
};
r.run();

Runnable l = () -> {
    // int x = 30; // Erro de compilação: a variável já está definida
    System.out.println(x); // 10
};
l.run();

7. Quando a lambda é melhor e quando a classe anônima é indispensável?

Lambdas — escolha‑as se:

  • for necessário implementar uma função curta para uma interface funcional;
  • não for preciso armazenar estado;
  • não precisar sobrescrever métodos de Object;
  • a implementação for “aqui e agora” e simples.

Classe anônima — necessária se:

  • for preciso implementar uma interface com vários métodos ou uma classe abstrata;
  • for necessário declarar campos ou métodos adicionais;
  • for preciso sobrescrever toString, equals, hashCode;
  • for necessário acesso a membros protegidos da superclasse.

8. Prática: comparação em exemplos

Tarefa 1: Filtrar uma lista com Predicate

Classe anônima:

List<String> animals = Arrays.asList("boi", "urso", "lobo", "tigre");
animals.removeIf(new Predicate<String>() {
    @Override
    public boolean test(String s) {
        return s.length() < 4;
    }
});
System.out.println(animals); // [urso, lobo, tigre]

Expressão lambda:

List<String> animals = Arrays.asList("boi", "urso", "lobo", "tigre");
animals.removeIf(s -> s.length() < 4);
System.out.println(animals); // [urso, lobo, tigre]

Tarefa 2: Comparando o escopo de this

public class Demo {
    String name = "Demo";

    void check() {
        Runnable anon = new Runnable() {
            String name = "Anon";
            @Override
            public void run() {
                System.out.println(this.name); // "Anon"
            }
        };

        Runnable lambda = () -> System.out.println(this.name); // "Demo"

        anon.run();
        lambda.run();
    }

    public static void main(String[] args) {
        new Demo().check();
    }
}

9. Erros típicos ao trabalhar com classes anônimas e lambdas

Erro nº 1: Esperar que uma lambda possa implementar vários métodos. Lambda funciona apenas com interfaces funcionais (um método abstrato). Se houver mais métodos — use uma classe anônima.

Erro nº 2: Confusão com o escopo de this. Em uma lambda, this é a classe externa; em uma classe anônima, é a própria classe anônima. Isso pode levar facilmente a “campos e valores errados”.

Erro nº 3: Tentar declarar campos em uma lambda. Em lambdas não é possível declarar campos próprios — apenas usar variáveis do contexto externo (final/“efetivamente final”). Para estado, use uma classe anônima.

Erro nº 4: Sombreamento de variáveis. Em uma classe anônima, é possível declarar uma variável local com o mesmo nome da externa — isso é sombreamento. Em lambdas isso não é permitido: o compilador emitirá um erro.

Erro nº 5: Lógica complexa demais na lambda. Se o corpo da lambda ficar maior que 35 linhas, a legibilidade sofre. É melhor extrair o código para um método separado ou usar uma classe anônima (se precisar de estado/vários métodos).

1
Pesquisa/teste
Expressões lambda, nível 48, lição 4
Indisponível
Expressões lambda
Expressões lambda
Comentários
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION