15 Generics e coleções
List, Set e Map com contratos de tipo
15.1 Objetivos de aprendizagem
Ao concluir este capítulo, você deverá ser capaz de:
- escolher entre
List,SeteMap; - explicar como generics evitam casts inseguros;
- criar uma classe genérica simples;
- registrar comandos por nome;
- reconhecer coleções mutáveis e imutáveis.
15.2 Escolha da coleção
| Estrutura | Use quando |
|---|---|
List<E> |
ordem e repetição importam |
Set<E> |
elementos não podem se repetir |
Map<K, V> |
um valor é localizado por uma chave |
A interface descreve o contrato; a implementação define detalhes de desempenho e ordenação. Por exemplo, HashSet costuma oferecer busca eficiente, mas a interface Set sozinha não promete uma implementação específica.
15.3 Registro de comandos com Map
import java.util.LinkedHashMap;
import java.util.Locale;
import java.util.Map;
interface Comando {
String nome();
String executar();
}
final class RegistroComandos {
private final Map<String, Comando> comandos = new LinkedHashMap<>();
void registrar(Comando comando) {
String chave = comando.nome().toLowerCase(Locale.ROOT);
if (comandos.putIfAbsent(chave, comando) != null) {
throw new IllegalArgumentException("Comando duplicado: " + chave);
}
}
String executar(String entrada) {
String chave = entrada.strip().toLowerCase(Locale.ROOT);
Comando comando = comandos.get(chave);
return comando == null
? "Comando desconhecido."
: comando.executar();
}
}O mapa substitui a busca linear: o nome normalizado funciona como chave e o objeto polimórfico funciona como valor. Não é necessário introduzir Runnable; o contrato Comando expressa melhor o domínio.
15.4 Classe genérica
T representa um tipo escolhido por quem usa a classe. Repositorio<String> aceita textos; Repositorio<Comando> aceita comandos. O compilador impede misturas indevidas.
15.5 Exemplo completo
import java.util.LinkedHashMap;
import java.util.Locale;
import java.util.Map;
public class Capitulo14Generics {
interface Comando {
String executar();
}
private final Map<String, Comando> comandos = new LinkedHashMap<>();
void registrar(String nome, Comando comando) {
String chave = nome.toLowerCase(Locale.ROOT);
if (comandos.putIfAbsent(chave, comando) != null) {
throw new IllegalArgumentException("Comando duplicado.");
}
}
String processar(String entrada) {
Comando comando =
comandos.get(entrada.strip().toLowerCase(Locale.ROOT));
return comando == null ? "Comando desconhecido." : comando.executar();
}
public static void main(String[] args) {
Capitulo14Generics registro = new Capitulo14Generics();
registro.registrar("ajuda", () -> "Comandos disponíveis.");
registro.registrar("dica", () -> "Use Map para localizar por chave.");
System.out.println(registro.processar("DICA"));
}
}A expressão lambda implementa a única operação de Comando. Ela aparece aqui somente depois de o contrato ter sido estudado; uma classe nomeada continua válida quando o comportamento exigir estado ou explicação adicional.
List.of e Map.of criam coleções que não aceitam alterações. Use-as para dados fixos. Para registros que crescem durante a execução, escolha uma implementação mutável, como ArrayList ou LinkedHashMap.
O exemplo completo está em examples/src/Capitulo14Generics.java.
15.6 Prática verificável
- Rejeite nomes de comando vazios.
- Impeça duplicação de comandos.
- Crie um
Set<String>com categorias de dicas. - Use
Repositorio<String>para armazenar perguntas de quiz.
Casos de teste:
- registrar a mesma chave duas vezes lança exceção;
" DICA "encontra o comandodica;- comando desconhecido devolve a mensagem padrão;
- a lista retornada pelo repositório não altera o estado interno.