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 3–5 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).
GO TO FULL VERSION