0% acharam este documento útil (0 voto)
25 visualizações8 páginas

Java Generics

Os generics em Java, introduzidos no Java 5, permitem a criação de classes, métodos e interfaces que operam de forma genérica em relação a tipos de dados, aumentando a segurança de tipos e a reutilização de código. O documento aborda conceitos como classes e métodos genéricos, parâmetros de tipo limitados, curingas, inferência de tipo e boas práticas, além de apresentar exemplos práticos e diagramas UML. A conclusão destaca a importância dos generics para evitar erros de tipo em tempo de execução e para a criação de APIs mais flexíveis e seguras.

Enviado por

carloscadupls
Direitos autorais
© © All Rights Reserved
Levamos muito a sério os direitos de conteúdo. Se você suspeita que este conteúdo é seu, reivindique-o aqui.
Formatos disponíveis
Baixe no formato PDF, TXT ou leia on-line no Scribd
0% acharam este documento útil (0 voto)
25 visualizações8 páginas

Java Generics

Os generics em Java, introduzidos no Java 5, permitem a criação de classes, métodos e interfaces que operam de forma genérica em relação a tipos de dados, aumentando a segurança de tipos e a reutilização de código. O documento aborda conceitos como classes e métodos genéricos, parâmetros de tipo limitados, curingas, inferência de tipo e boas práticas, além de apresentar exemplos práticos e diagramas UML. A conclusão destaca a importância dos generics para evitar erros de tipo em tempo de execução e para a criação de APIs mais flexíveis e seguras.

Enviado por

carloscadupls
Direitos autorais
© © All Rights Reserved
Levamos muito a sério os direitos de conteúdo. Se você suspeita que este conteúdo é seu, reivindique-o aqui.
Formatos disponíveis
Baixe no formato PDF, TXT ou leia on-line no Scribd

Generics

A utilização de generics (ou “tipos genéricos”) em Java é um recurso introduzido a partir do Java 5
para oferecer maior segurança de tipos (type safety) e reutilização de código. Com generics,
podemos criar classes, métodos e interfaces que funcionam de maneira “genérica” em relação ao
tipo de dados que manipulam, evitando a necessidade de casts explícitos e diminuindo a chance
de erros de runtime.
A seguir, apresentaremos os principais tópicos relacionados aos genéricos em Java, com exemplos
de código e, se necessário, um diagrama UML para reforçar a compreensão.

Índice

1. Motivação e Introdução aos Generics


2. Classes Genéricas
3. Métodos Genéricos
4. Bounded Type Parameters
5. Wildcards (`?`)
6. Type Inference e Diamond Operator
7. Type Erasure
8. Exemplo Prático e Diagrama com PlantUML
9. Boas Práticas
10. Conclusão

1. Motivação e Introdução aos Generics


Antes da introdução dos genéricos, muitas classes e estruturas de dados (como as coleções em
Java) trabalhavam com `Object`, obrigando o desenvolvedor a fazer casts explícitos para o tipo
desejado ao recuperar objetos. Isso abria espaço para erros de tempo de execução, pois o
compilador não detectava incompatibilidades de tipo.

Com generics, podemos declarar, por exemplo, uma `List<String>` e ter a garantia em tempo de
compilação de que apenas `String` poderá ser adicionado nessa lista, eliminando a necessidade de
casts e gerando código mais con ável.

2. Classes Genéricas

Declaração
Para declarar uma classe genérica, usamos type parameters (normalmente `<T>`, `<E>`, `<K, V>`,
etc.):
public class Caixa<T> {
private T conteudo;

public void guardar(T item) {


[Link] = item;
}

public T abrir() {
return conteudo;
}
}
• `T` é o parâmetro de tipo.
• A classe `Caixa` agora pode armazenar qualquer tipo de objeto em `conteudo`, mas, ao
instanciar a classe, de nimos qual será o tipo real.
fi
fi
Generics
Instanciando e Usando
public class Main {
public static void main(String[] args) {
Caixa<String> caixaDeString = new Caixa<>();
[Link]("Olá, generics!");
String valor = [Link](); // não precisa de cast
[Link](valor);

Caixa<Integer> caixaDeInteger = new Caixa<>();


[Link](42);
Integer numero = [Link](); // não precisa de cast
[Link](numero);
}
}
Quando escrevemos `new Caixa<String>()`, o compilador infere que `T` passa a ser `String`. Já na
`Caixa<Integer>`, `T` torna-se `Integer`. Em ambos os casos, temos segurança de tipos.

3. Métodos Genéricos
Além de classes genéricas, podemos ter métodos genéricos dentro de classes “normais” (ou
mesmo dentro de classes genéricas).

Exemplo de um Método Genérico


public class Utilitario {

// <T> declara o tipo genérico do método,


// que pode ser usado nos parâmetros e no retorno.
public static <T> void exibirArray(T[] array) {
for (T elemento : array) {
[Link](elemento + " ");
}
[Link]();
}
}
Uso do método genérico
public class MainMetodoGenerico {
public static void main(String[] args) {
String[] nomes = { "Ana", "Bia", "Carlos" };
Integer[] numeros = { 1, 2, 3, 4 };

[Link](nomes); // chama <String> void


exibirArray(String[])
[Link](numeros); // chama <Integer> void
exibirArray(Integer[])
}
}
Generics
Note que não precisamos especi car explicitamente `<String>` ou `<Integer>` ao chamar
`exibirArray`, porque o compilador infere o tipo a partir do parâmetro.

4. Bounded Type Parameters


Às vezes, é útil restringir o tipo genérico para uma hierarquia especí ca ou para classes que
implementam determinada interface. Para isso, usamos `extends` em parâmetros de tipo.

Exemplo: Restrição a `Number`


public class Calculadora<T extends Number> {
private T numero1;
private T numero2;

public Calculadora(T numero1, T numero2) {


this.numero1 = numero1;
this.numero2 = numero2;
}

public double somar() {


// Podemos chamar métodos de Number, como doubleValue()
return [Link]() + [Link]();
}
}
• `T extends Number` signi ca que `T` deve ser `Number` ou subclasse de `Number` (por exemplo,
`Integer`, `Double`, `Float`, etc.).
• Assim, temos certeza de que o método `doubleValue()` existe.

Uso:
public class MainBounded {
public static void main(String[] args) {
Calculadora<Integer> calcInt = new Calculadora<>(10, 20);
Calculadora<Double> calcDouble = new Calculadora<>(3.5, 4.7);

[Link]("Soma int: " + [Link]());


[Link]("Soma double: " + [Link]());

// Calculadora<String> calcString = new Calculadora<>("a",


"b"); // ERRO de compilação
}
}
O último exemplo (comentado) não compila porque `String` não é uma subclasse de `Number`.

5. Wildcards (`?`)
Wildcards (ou curingas) são usados quando precisamos de exibilidade para aceitar diferentes tipos
genéricos, mas não precisamos (ou não podemos) xar um tipo especí co. O coringa é
representado por `?`.
Existem três principais variações:
1. Unbounded Wildcard (`?`)
- Signi ca “qualquer tipo”.
- Exemplo: `List<?>` pode ser uma lista de qualquer tipo (`List<String>`, `List<Integer>`,
etc.).
fi
fi
fi
fi
fl
fi
fi
Generics
2. Bounded Wildcard (Upper Bound) (`? extends T`)
- Signi ca “algum tipo que é `T` ou subclasse de `T`”.
- Exemplo: `List<? extends Number>` aceita `List<Integer>`, `List<Double>`, etc.

3. Bounded Wildcard (Lower Bound) (`? super T`)


- Signi ca “algum tipo que é `T` ou superclasse de `T`”.
- Exemplo: `List<? super Number>` aceita `List<Number>`, `List<Object>`, etc.

Exemplo de Uso do Unbounded Wildcard


public class WildcardExemplo {

public static void imprimirLista(List<?> lista) {


// Não podemos inserir valores na lista (exceto null) porque não
sabemos o tipo exato.
for (Object obj : lista) {
[Link](obj + " ");
}
[Link]();
}

public static void main(String[] args) {


List<String> listaString = [Link]("A", "B", "C");
List<Integer> listaInteger = [Link](1, 2, 3);

imprimirLista(listaString); // Aceito
imprimirLista(listaInteger); // Aceito
}
}
Observe que dentro do método `imprimirLista`, não sabemos o tipo exato dos elementos, por isso
só podemos tratá-los como `Object`.

Exemplo de `? extends T`
public class WildcardExtendsExemplo {

public static double somarLista(List<? extends Number> lista) {


double soma = 0.0;
for (Number num : lista) {
soma += [Link]();
}
return soma;
}

public static void main(String[] args) {


List<Integer> ints = [Link](1, 2, 3);
List<Double> doubles = [Link](1.5, 2.2, 3.3);

[Link](somarLista(ints)); // OK: ? extends Number


[Link](somarLista(doubles)); // OK: ? extends Number
}
}
fi
fi
Generics
Exemplo de `? super T`
public class WildcardSuperExemplo {

public static void inserirNumeros(List<? super Integer> lista) {


// Podemos inserir Integers (ou subclasses) numa lista que aceite
Integer ou superclasse
[Link](10);
[Link](20);
}

public static void main(String[] args) {


List<Number> numeros = new ArrayList<>();
inserirNumeros(numeros); // OK: List<Number> é super de Integer

List<Object> objetos = new ArrayList<>();


inserirNumeros(objetos); // OK: List<Object> é super de Integer
}
}

6. Type Inference e Diamond Operator


Desde o Java 7, podemos usar o Diamond Operator (`<>`) na hora de instanciar coleções ou classes
genéricas para não precisar repetir o parâmetro de tipo:
List<String> lista = new ArrayList<>();
//Em vez de: new ArrayList<String>()
• O compilador infere que o tipo do `ArrayList` é `String` a partir do lado esquerdo da atribuição.
Em Java 8 e superiores, a inferência de tipo melhorou ainda mais, permitindo que se use generics
em contextos de métodos estáticos, lambdas, etc., sem precisar especi car os parâmetros de tipo
explicitamente.

7. Type Erasure
O Java implementa genéricos através de um processo chamado type erasure. Em tempo de
compilação, todas as veri cações de tipo são feitas; porém, em tempo de execução, as
informações de tipo genérico são removidas (erased). Isso signi ca, por exemplo, que:

• `List<String>` e `List<Integer>` se tornam `List` em tempo de execução, sem distinção de


tipo.
• Métodos que utilizam parâmetros genéricos podem ter bridge methods gerados pelo
compilador para manter compatibilidade binária.
A consequência é que não podemos fazer re ection do tipo genérico em tempo de execução da
mesma forma que em linguagens como C#. E também há restrições, por exemplo, não é possível
criar arrays de tipos genéricos diretamente (`new T[]`), a não ser por workaround.

8. Exemplo Prático e Diagrama com UML


Imaginemos um repositório genérico que pode armazenar qualquer entidade, contanto que ela
tenha um `ID`. Vamos criar uma interface `Identi cavel` e um repositório `Repositorio<T>` que use
generics para manipular qualquer tipo que implemente `Identi cavel`.

Código
fi
fl
fi
fi
fi
fi
Generics
Interface `Identi cavel`
public interface Identificavel {
String getId();
}
Classe de Entidade (Exemplo: `Produto`)
public class Produto implements Identificavel {
private String id;
private String nome;

public Produto(String id, String nome) {


[Link] = id;
[Link] = nome;
}

@Override
public String getId() {
return id;
}

public String getNome() {


return nome;
}
}
Classe Genérica `Repositorio<T extends Identi cavel>`
import [Link];
import [Link];

public class Repositorio<T extends Identificavel> {


private Map<String, T> dados = new HashMap<>();

public void salvar(T objeto) {


[Link]([Link](), objeto);
}

public T buscarPorId(String id) {


return [Link](id);
}

public void remover(String id) {


[Link](id);
}
}
fi
fi
Generics
Exemplo de Uso
public class MainRepositorio {
public static void main(String[] args) {
Repositorio<Produto> repoProdutos = new Repositorio<>();

Produto p1 = new Produto("001", "Laptop");


Produto p2 = new Produto("002", "Smartphone");

[Link](p1);
[Link](p2);

Produto res = [Link]("001");


[Link]([Link]()); // "Laptop"

[Link]("002");
}
}
Diagrama UML

• `Identi cavel` é uma interface que `Produto` implementa.


• `Repositorio` é uma classe genérica com `<T extends Identi cavel>`.

9. Boas Práticas
1. Nomes de Parâmetros de Tipo: Use letras como `T`, `E`, `K`, `V` apenas para casos simples
(por exemplo, coleções). Para casos mais complexos, considere nomes descritivos
(`<Cliente>`, `<Entidade>`), melhorando a legibilidade.
2. Evite Erros de Heap Pollution: Não retorne ou injetar arrays de tipos parametrizados, pois
devido ao type erasure, podem ocorrer problemas em tempo de execução.
3. Pre ra Generics a `Object`: Sempre que puder, utilize generics em vez de coleções ou
parâmetros tipados como `Object`. Isso garante melhor segurança de tipos.
4. Use Bounded Wildcards de Forma Consciente: Ao criar APIs, pense cuidadosamente em
onde utilizar `? extends` e `? super` para permitir maior exibilidade a quem for usar sua
API.
5. Não Abusar do `?` Unbounded: Se a intenção é usar um tipo especí co ou uma sub-
hierarquia especí ca, considere `? extends T` ou `? super T`.
6. Polimor smo e Generics: Lembre-se de que `List<Object>` não é supertipo de
`List<String>` (generics não funcionam por subtipagem de covariância em Java). Use
wildcards quando precisar lidar com coleções heterogêneas.

10. Conclusão
Os generics em Java são uma das bases para escrever código seguro em termos de tipos (type-
safe) e mais expressivo. Eles ajudam:
• A evitar casts desnecessários.
fi
fi
fi
fi
fl
fi
fi
Generics
• A capturar erros de tipo em tempo de compilação, antes que se tornem problemas em
produção.
• A criar APIs e bibliotecas que funcionam para múltiplos tipos, sem duplicar código.
O funcionamento sob o capô ocorre por meio de type erasure, mas, do ponto de vista do
desenvolvedor, generics oferecem uma poderosa ferramenta para a criação de classes, métodos e
interfaces reusáveis e exíveis.
Aprofundando-se em tópicos como bounded type parameters e wildcards, é possível construir APIs
robustas e facilitar a evolução do sistema, mantendo a clareza e a segurança de tipos.
fl

Você também pode gostar