Atualizado: 16/08/2025

Este conteúdo é original e não foi gerado por inteligência artificial.

Generics em Java: Tipos e Métodos Genéricos

Em Java, Generics (ou obrigatoriedade de tipos genéricos) permitem criar classes, interfaces e métodos que funcionam com diferentes tipos de dados, mantendo segurança de tipos e evitando conversões desnecessárias.

O grande objetivo é não amarrar o código a um tipo específico quando não sabemos de antemão qual será usado.


O problema sem generics

Vamos supor que temos um Account que guarda o ID da conta e o saldo:

class Account {
    private int id;
    private int sum;

    Account(int id, int sum) {
        this.id = id;
        this.sum = sum;
    }

    public int getId() { return id; }
    public int getSum() { return sum; }
    public void setSum(int sum) { this.sum = sum; }
}

Aqui, o id foi definido como int. Mas em alguns sistemas, IDs são strings (ex.: "A9837") ou até objetos mais complexos. Se quisermos aceitar qualquer tipo, poderíamos mudar id para Object:

class Account {
    private Object id;
    private int sum;

    Account(Object id, int sum) {
        this.id = id;
        this.sum = sum;
    }

    public Object getId() { return id; }
    public int getSum() { return sum; }
    public void setSum(int sum) { this.sum = sum; }
}

Isso “funciona”, mas perde a segurança de tipos. Veja:

Account acc1 = new Account("2345", 5000);
int id = (int) acc1.getId(); // Erro em tempo de execução (ClassCastException)

O compilador não nos avisa que getId() não retorna um int. Esse é o tipo de problema que Generics resolvem.


Classe genérica

Com Generics, podemos dizer que Account aceitará um tipo genérico T para o ID:

class Account<T> {
    private T id;
    private int sum;

    Account(T id, int sum) {
        this.id = id;
        this.sum = sum;
    }

    public T getId() { return id; }
    public int getSum() { return sum; }
    public void setSum(int sum) { this.sum = sum; }
}

Uso:

public class Program {
    public static void main(String[] args) {
        Account<String> acc1 = new Account<>("2345", 5000);
        String id1 = acc1.getId(); // não precisa cast
        System.out.println(id1);

        Account<Integer> acc2 = new Account<>(2345, 5000);
        Integer id2 = acc2.getId();
        System.out.println(id2);
    }
}

Vantagens:

  • O compilador sabe qual tipo está sendo usado.
  • Erros como ClassCastException são detectados em tempo de compilação.
  • O código fica mais flexível e reutilizável.

⚠️ Generics não funcionam com tipos primitivos (int, double etc.). Se precisar, use classes wrapper (Integer, Double, etc.).


Generics em interfaces

Interfaces também podem ser genéricas:

interface Accountable<T> {
    T getId();
    int getSum();
    void setSum(int sum);
}

Implementação com tipo fixo

Podemos fixar o tipo ao implementar:

class Account implements Accountable<String> {
    private String id;
    private int sum;

    Account(String id, int sum) {
        this.id = id;
        this.sum = sum;
    }

    public String getId() { return id; }
    public int getSum() { return sum; }
    public void setSum(int sum) { this.sum = sum; }
}

Uso:

Accountable<String> acc = new Account("123", 5000);
System.out.println(acc.getId());

Implementação mantendo o tipo genérico

Outra abordagem é deixar a classe também genérica:

class Account<T> implements Accountable<T> {
    private T id;
    private int sum;

    Account(T id, int sum) {
        this.id = id;
        this.sum = sum;
    }

    public T getId() { return id; }
    public int getSum() { return sum; }
    public void setSum(int sum) { this.sum = sum; }
}

Uso:

Account<String> acc1 = new Account<>("123", 5000);
Account<Integer> acc2 = new Account<>(456, 3000);

Métodos genéricos

Além de classes e interfaces, métodos também podem ser genéricos:

class Printer {
    public <T> void print(T[] items) {
        for (T item : items) {
            System.out.println(item);
        }
    }
}

Uso:

Printer p = new Printer();
p.<String>print(new String[] {"Tom", "Alice"});
p.<Integer>print(new Integer[] {1, 2, 3});

O <T> antes do tipo de retorno indica que o método é genérico.


Múltiplos parâmetros genéricos

Podemos usar mais de um tipo genérico:

class Account<T, S> {
    private T id;
    private S sum;

    Account(T id, S sum) {
        this.id = id;
        this.sum = sum;
    }

    public T getId() { return id; }
    public S getSum() { return sum; }
    public void setSum(S sum) { this.sum = sum; }
}

Uso:

Account<String, Double> acc = new Account<>("123", 5000.75);

Construtores genéricos

Até construtores podem ter parâmetros genéricos próprios:

class Account {
    private String id;
    private int sum;

    <T> Account(T id, int sum) {
        this.id = id.toString();
        this.sum = sum;
    }

    public String getId() { return id; }
}

Aqui, o construtor aceita qualquer tipo para id e converte para String.


Resumo

  • Permitem criar classes, interfaces e métodos que funcionam com diferentes tipos, mantendo segurança de tipos.
  • Evitam o uso de casting manual e tornam o código mais legível.
  • São definidos por parâmetros de tipo como <T>, que podem ser usados em variáveis, parâmetros e retornos.
  • Mantêm a verificação de tipos em tempo de compilação, prevenindo erros de execução.
  • Podem ser combinados com herança e interfaces para criar estruturas mais flexíveis.
Política de Privacidade

Copyright © www.programicio.com Todos os direitos reservados

É proibida a reprodução do conteúdo desta página sem autorização prévia do autor.

Contato: programicio@gmail.com