Parametrização de Tipos com Generics
Generics permitem a parametrização de tipos de dados, tratando tipos como parâmetros. Ao utilizar um tipo genérico, você especifica o tipo concreto a ser usado.
Generics em Classes
Ao declarar um tipo genérico para uma classe, todos os membros não estáticos dentro dessa classe podem utilizar o parâmetro de tipo.
A sintaxe para definir uma classe genérica é:
```
public class NomeClasse<T, A, B, C> { private T variavel;
public T metodo(T nomeParametro, ...) {
// corpo do método
}
}
O parâmetro de tipo em uma classe genérica determina os tipos de dados das variáveis de instância e dos tipos de retorno dos métodos da classe.
Veja um exemplo de uma classe `Fruta` genérica:
```
public class Fruta<T> {
private T fruta;
public void definirFruta(T fruta) {
this.fruta = fruta;
}
public T obterFruta() {
return fruta;
}
}
E uma classe Maca:
```
public class Maca { @Override public String toString() { return "Maçã"; } }
Exemplo de teste:
```
public class TesteGenerics {
public static void main(String[] args) {
Fruta<Maca> f1 = new Fruta<>();
f1.definirFruta(new Maca());
System.out.println(f1.obterFruta()); // Saída: Maçã
}
}
Generics em Métodos
Métodos também podem ter seus próprios parâmetros de tipo, independentes dos tipos genéricos da classe onde estão definidos (se houver).
Sintaxe:
```
// Método com retorno public <T> T metodoComRetorno(T parametro) { ... }
// Método sem retorno public <T> void metodoSemRetorno(T parametro) { ... }
Exemplo prático:
```
public class Maca {
public <T> T obterDado(T dado) {
return dado;
}
public <T> void imprimirDado(T dado) {
System.out.println(dado);
}
}
Teste:
```
public class TesteGenerics { public static void main(String[] args) { Maca maca = new Maca(); maca.imprimirDado(123); // Saída: 123 System.out.println(maca.obterDado("texto")); // Saída: texto } }
### Generics em Interfaces
Assim como em classes, interfaces podem ser definidas com parâmetros de tipo, comumenet nomeados como `T`.
Definição:
```
public interface NomeInterface<T, A, B, C> {
T metodo1();
}
Ao implementar uma interface genérica, você pode fornecer os tipos concretos ou deixar a implementação genérica.
Implementação com tipo concreto:
```
public class MinhaClasse implements NomeInterface<TipoConcreto1, TipoConcreto2, TipoConcreto3> { @Override public TipoConcreto1 metodo1() { // implementação } }
Implementação mantendo a generalidade (a classe que implementa também se torna genérica):
```
public class MinhaClasse<T> implements NomeInterface<T> {
@Override
public T metodo1() {
// implementação
}
}
Restrições em Generics (Wildcards)
Restrições podem ser aplicadas a parâmetros de tipo para limitar os tipos que podem ser usados.
- Upper Bound (Limite Superior): Restringe o tipo genérico a uma classe específica ou suas subclasses. Sintaxe:
<T extends ClasseOuInterface>. - Lower Bound (Limite Inferior): Restringe o tipo genérico a uma classe específica ou suas superclasses. Sintaxe:
<T super ClasseOuInterface>.
Wildcards em Generics
Wildcards (?) são usados para indicar um tipo desconhecido ou para aplicar restrições de limite superior/inferior em um contexto específico, como em parâmetros de métodos ou atribuições.
Exemplo ilustrando limites superior e inferior:
```
public class ExemploWildcard { public static void main(String[] args) { // Limite superior: ? aceita classes que são subtipos de Desperdicio Lixeira<? extends Desperdicio> lixeira1 = new Lixeira<ResiduoMedico>(); Lixeira<? extends Desperdicio> lixeira2 = new Lixeira<ResiduoOrganico>();
// Limite inferior: ? aceita classes que são supertipos de ResiduoOrganico
Lixeira<? super ResiduoOrganico> lixeira3 = new Lixeira<Desperdicio>();
Lixeira<? super ResiduoOrganico> lixeira4 = new Lixeira<ResiduoOrganico>();
}
}
interface Desperdicio {}
interface NaoReciclavel extends Desperdicio {}
class Lixeira<T, C> { // ... implementação }
class ResiduoMedico implements NaoReciclavel { }
class ResiduoOrganico implements NaoReciclavel { }