Introdução
Genéricos em Java permitem escrever código flexível e reutilizável, fornecendo uma camada adicional de abstração. Eles permitem criar um único algoritmo para múltiplos tipos de objetos. Genéricos fornecem segurança de tipo e evitam erros em tempo de execução. Neste laboratório, entenderemos vários aspectos dos Genéricos em Java.
Criando uma Classe Genérica
Podemos criar uma classe Genérica em Java com a ajuda do operador diamante (<>). No bloco de código a seguir, criaremos uma classe Genérica chamada MyGenericClass que pode aceitar qualquer tipo de objeto. Também criaremos um objeto do tipo MyGenericClass com tipos inteiro e string como parâmetros.
// ~/project/MyGenericClass.java
class MyGenericClass<T> {
T field1;
public MyGenericClass(T field1) {
this.field1 = field1;
}
public T getField1() {
return field1;
}
}
public class Main {
public static void main(String[] args) {
MyGenericClass<Integer> myIntObj = new MyGenericClass<Integer>(100);
MyGenericClass<String> myStringObj = new MyGenericClass<String>("Hello World");
System.out.println(myIntObj.getField1());
System.out.println(myStringObj.getField1());
}
}
Para executar o código, abra o terminal e navegue até o diretório ~/project. Compile o código com o seguinte comando:
javac MyGenericClass.java
Após a compilação bem-sucedida, execute o código com o seguinte comando:
java Main
A saída do código será:
100
Hello World
Criando um Método Genérico
Também podemos criar um método Genérico em Java. O método Genérico pode trabalhar com diferentes tipos de objetos. No bloco de código a seguir, criaremos um método Genérico chamado printArray que pode imprimir qualquer tipo de array.
// ~/project/Main.java
public class Main {
public static <T> void printArray(T[] array) {
for (T element : array)
System.out.println(element);
}
public static void main(String[] args) {
Integer[] intArray = {1, 2, 3, 4, 5};
Double[] doubleArray = {1.1, 2.2, 3.3, 4.4};
String[] stringArray = {"Hello", "World"};
printArray(intArray);
printArray(doubleArray);
printArray(stringArray);
}
}
Para executar o código, abra o terminal e navegue até o diretório ~/project. Compile o código com o seguinte comando:
javac Main.java
Após a compilação bem-sucedida, execute o código com o seguinte comando:
java Main
A saída do código será:
1
2
3
4
5
1.1
2.2
3.3
4.4
Hello
World
Criando um Método Genérico com Limites (Bounded)
Também podemos criar um método Genérico Limitado. Genéricos Limitados restringem a gama de tipos aceitos pelo método. Usamos a palavra-chave extends para impor o limite (bound). No bloco de código a seguir, criaremos um método Genérico Limitado chamado printNumbers que pode imprimir os números de objetos do tipo Number e suas subclasses.
// ~/project/Main.java
public class Main {
public static <T extends Number> void printNumbers(T[] numbers) {
for (T number : numbers)
System.out.println(number);
}
public static void main(String[] args) {
Integer[] intNumbers = {1, 2, 3, 4, 5};
Double[] doubleNumbers = {1.1, 2.2, 3.3, 4.4};
printNumbers(intNumbers);
printNumbers(doubleNumbers);
}
}
Para executar o código, abra o terminal e navegue até o diretório ~/project. Compile o código com o seguinte comando:
javac Main.java
Após a compilação bem-sucedida, execute o código com o seguinte comando:
java Main
A saída do código será:
1
2
3
4
5
1.1
2.2
3.3
4.4
Entendendo a Segurança de Tipos em Generics
Genéricos fornecem segurança de tipos, garantindo que definimos apenas um tipo de objeto para trabalhar por vez. No bloco de código a seguir, criaremos um ArrayList de inteiros sem definir seu parâmetro de tipo. É uma má prática usar Genéricos sem parâmetros de tipo. O código compilará, mas retornará um erro em tempo de execução quando tentarmos acessar seus elementos de outros tipos.
// ~/project/Main.java
import java.util.ArrayList;
public class Main {
public static void main(String[] args) {
ArrayList integerList = new ArrayList();
integerList.add(1);
integerList.add(2);
integerList.add(3);
integerList.add("Hello World");
for (Object element : integerList) {
System.out.println((Integer)element);
}
}
}
Para executar o código, abra o terminal e navegue até o diretório ~/project. Compile o código com o seguinte comando:
javac Main.java
Após a compilação bem-sucedida, execute o código com o seguinte comando:
java Main
A saída do código será:
1
2
3
Exception in thread "main" java.lang.ClassCastException: java.base/java.lang.String cannot be cast to java.base/java.lang.Integer
at Main.main(Main.java:13)
Obtemos um erro em tempo de execução porque o integerList contém elementos do tipo Integer e do tipo String. Isso ocorre porque não definimos o parâmetro de tipo ao criar o integerList.
Entendendo a Type Erasure em Generics
Java usa a Eliminação de Tipos (Type Erasure) em Genéricos para garantir que nenhuma sobrecarga adicional seja necessária em tempo de execução. O compilador substitui o parâmetro de tipo pela classe Object ou pela classe pai (no caso de Genéricos limitados) durante o processo de eliminação de tipos. No bloco de código a seguir, veremos como os Genéricos funcionam com tipos primitivos em tempo de compilação e tempo de execução.
// ~/project/Main.java
import java.util.ArrayList;
public class Main {
public static void main(String[] args) {
ArrayList<Integer> integerList = new ArrayList<Integer>();
integerList.add(1);
integerList.add(2);
integerList.add(3);
int sum = 0;
for (int i = 0; i < integerList.size(); i++) {
sum += integerList.get(i);
}
System.out.println(sum);
}
}
Para executar o código, abra o terminal e navegue até o diretório ~/project. Compile o código com o seguinte comando:
javac Main.java
Após a compilação bem-sucedida, execute o código com o seguinte comando:
java Main
A saída do código será:
6
No bloco de código acima, tentamos adicionar os elementos do integerList. Como sabemos, tipos primitivos como int não podem ser usados com Genéricos. Devido à eliminação de tipos, o parâmetro de tipo Integer é substituído pela classe Object em tempo de compilação. No entanto, autoboxing e unboxing nos permitiram realizar a adição de inteiros em tempo de compilação. Em tempo de execução, o código foi executado como uma execução de código normal, sem qualquer sobrecarga adicional.
Resumo
Neste laboratório, aprendemos sobre Genéricos em Java. Cobrimos os conceitos básicos relacionados a classes genéricas, métodos genéricos, métodos genéricos limitados, segurança de tipos em genéricos e eliminação de tipos (Type Erasure) em genéricos. Também vimos como escrever código flexível e reutilizável usando Genéricos. Esperamos que este laboratório tenha ajudado você a entender Genéricos em Java.



