CodeGym /Cursos /JAVA 25 SELF /Generics: por que são necessários, sintaxe básica

Generics: por que são necessários, sintaxe básica

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

1. O problema das coleções “brutas” (raw types)

Vamos mergulhar rapidamente na história. Antes do Java 5, todas as coleções eram “onívoras”. Elas armazenavam objetos do tipo Object, e o compilador não controlava o que exatamente você colocava nelas. Quer colocar uma string? Por favor. Um número? Por que não. Um gato? Também pode.

// Exemplo de coleções "brutas" (raw types), Java antes da versão 5
List list = new ArrayList();
list.add("Olá");
list.add(42);
list.add(new Object());

O problema aparecia ao “retirar” e usar o valor:

String s = (String) list.get(0); // OK, é uma String
String s2 = (String) list.get(1); // BOOM! ClassCastException

O compilador fica em silêncio, e em tempo de execução você recebe ClassCastException. É como uma caixa com a etiqueta “maçãs” que contém uma xícara, uma banana e um ouriço.

Por que isso é ruim?

  • Os erros só aparecem em tempo de execução.
  • Tipos se misturam: é preciso converter objetos manualmente (cast).
  • O código fica menos legível e mais perigoso.

A solução — generics (genéricos)

Generics (genéricos) são um mecanismo que permite criar classes, interfaces e métodos com parâmetros de tipo. Ou seja, você diz à coleção: “Armazene apenas strings”, e o compilador fiscaliza isso rigorosamente.

List<String> words = new ArrayList<>();
words.add("Olá");
words.add("Mundo");
// words.add(42); // Erro de compilação! Não é possível adicionar int a List<String>

Agora o compilador não vai permitir colocar na lista nada além de strings. O erro é detectado antes de executar o programa.

Ideia principal dos generics:
Garantir a segurança de tipos das coleções (e não só), para que os erros sejam capturados na fase de compilação, e não em tempo de execução.

2. Sintaxe de generics: como isso fica no código

Indicação do tipo entre os sinais “<>”

Ao criar uma coleção, indique o tipo dos elementos em <>:

List<String> names = new ArrayList<>();
names.add("Alice");
names.add("Bob");
// names.add(123); // Erro: não é possível adicionar um número a uma lista de strings

String first = names.get(0); // Não é necessário cast!

Clássicos:

  • List<String> — lista de strings
  • List<Integer> — lista de inteiros
  • Set<Double> — conjunto de números de ponto flutuante
  • Map<String, Integer> — chave String, valor Integer

Por que não escrever apenas List?

Pode, mas você perde todos os benefícios dos generics, e o compilador vai avisar:

List list = new ArrayList(); // raw type — não recomendado!
list.add("Hello");
list.add(7.5);
String s = (String) list.get(1); // Olá, ClassCastException!

Código Java moderno sempre usa generics.

Operador diamante <>

Desde o Java 7, você pode não especificar o tipo à direita se ele for óbvio pelo contexto:

List<String> list = new ArrayList<>(); // O compilador deduz que é <String> aqui

3. Generics para diferentes coleções

Exemplos para List, Set, Map

List<Integer> numbers = new ArrayList<>();
numbers.add(10);
numbers.add(20);

Set<String> uniqueNames = new HashSet<>();
uniqueNames.add("Alice");
uniqueNames.add("Bob");

Map<String, Integer> ages = new HashMap<>();
ages.put("Alice", 23);
ages.put("Bob", 31);

Exemplo com classe própria

class Student {
    String name;
    int age;
    // ...
}

List<Student> students = new ArrayList<>();
students.add(new Student());

4. Nuances úteis

Vantagens dos generics

Segurança de tipos. O compilador garante que apenas elementos do tipo correto entrem na coleção.

Não há necessidade de cast. Antes: String s = (String) list.get(0);. Agora: String s = list.get(0);.

Código mais legível e confiável. Menos surpresas em tempo de execução.

Limitações dos generics

Não é possível usar tipos primitivos. Generics funcionam apenas com objetos, não com primitivos (int, double, boolean). Use classes wrapper: Integer, Double, Boolean.

List<Integer> numbers = new ArrayList<>();
numbers.add(10); // int é convertido automaticamente para Integer (autoboxing)

Breve sobre apagamento de tipos (type erasure)

No Java, os generics são implementados por meio do mecanismo de apagamento de tipos: após a compilação, a informação sobre parâmetros de tipo é apagada, e em tempo de execução a JVM não sabe que aquilo era List<String>, e não simplesmente List. Isso foi feito por compatibilidade retroativa.

Consequência: não é possível verificar o parâmetro de tipo com instanceof usando um argumento de tipo específico.

List<String> list = new ArrayList<>();
// if (list instanceof List<String>) { ... } // Erro de compilação!

Tentar adicionar elemento de outro tipo — erro de compilação

List<String> words = new ArrayList<>();
words.add("Hello");
// words.add(123); // Erro de compilação: incompatible types: int cannot be converted to String

Map<String, Integer> map = new HashMap<>();
map.put("Gato", 5);
// map.put(3, "Elefante"); // Erro: a chave deve ser String, o valor — Integer

E isso é ótimo: os erros são capturados na fase de compilação.

Não apenas coleções

Generics podem ser usados em suas próprias classes e métodos. Por exemplo, uma “Caixa” genérica:

class Box<T> {
    private T value;

    public void set(T value) { this.value = value; }
    public T get() { return value; }
}

Box<String> stringBox = new Box<>();
stringBox.set("Olá");
System.out.println(stringBox.get());

Box<Integer> intBox = new Box<>();
intBox.set(42);
System.out.println(intBox.get());

Em coleções, os generics são padrão, mas você também os encontrará em outros lugares, como no Stream API e em Optional.

5. Erros comuns ao trabalhar com generics

Erro №1: uso de coleções “brutas”. Uma declaração como List list = new ArrayList(); remove a segurança de tipos. Sempre indique os parâmetros de tipo, por exemplo List<String>.

Erro №2: tentativa de usar primitivos. Não é possível escrever List<int>; use List<Integer>.

Erro №3: cast manual ao ler da coleção. Se você usa generics, o cast do tipo (String) list.get(i) não é necessário. Se for preciso — em algum lugar os tipos foram violados.

Erro №4: esperar que os parâmetros de tipo estejam disponíveis em tempo de execução. Devido ao apagamento de tipos, não é possível verificá-los com instanceof como em List<String>.

Erro №5: misturar tipos diferentes em uma mesma coleção. Se foi declarado List<String>, não tente adicionar Integer — o compilador não permitirá, e isso é bom.

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