CodeGym /Blogue Java /Random-PT /Genéricos em Java
John Squirrels
Nível 41
San Francisco

Genéricos em Java

Publicado no grupo Random-PT
Oi! Nós vamos falar sobre Java Generics. Devo dizer que você aprenderá muito! Não apenas esta lição, mas também as próximas lições serão dedicadas aos genéricos. Portanto, se você se interessa por genéricos, hoje é seu dia de sorte: aprenderá muito sobre as características dos genéricos. E se não, resigne-se e relaxe! :) Esse é um tema muito importante, e você precisa saber disso. Vamos começar com o simples: o "o quê" e o "porquê".

O que são Java Genéricos?

Genéricos são tipos que possuem um parâmetro. Ao criar um tipo genérico, você especifica não apenas um tipo, mas também o tipo de dados com o qual ele trabalhará. Acho que o exemplo mais óbvio já veio à sua mente: ArrayList! É assim que geralmente criamos um em um programa:

import java.util.ArrayList;
import java.util.List;

public class Main {

   public static void main(String[] args) {

       List<String> myList1 = new ArrayList<>();
       myList1.add("Test String 1");
       myList1.add("Test String 2");
   }
}
Como você pode imaginar, uma característica desta lista é que não podemos colocar tudo nela: ela funciona exclusivamente com objetos String . Agora vamos fazer uma pequena digressão na história do Java e tentar responder à pergunta "por quê?" Para fazer isso, escreveremos nossa própria versão simplificada da classe ArrayList. Nossa lista só sabe como adicionar dados e recuperar dados de um array interno:

public class MyListClass {

   private Object[] data;
   private int count;

   public MyListClass() {
       this.data = new Object[10];
       this.count = 0;
   }

   public void add(Object o) {
       this.data[count] = o;
       count++;
   }

   public Object[] getData() {
       return data;
   }
}
Suponha que queremos que nossa lista armazene apenas Integer s. Não estamos usando um tipo genérico. Não queremos incluir uma verificação "instanceof Integer " explícita no método add() . Se o fizéssemos, toda a nossa classe seria adequada apenas para Integer , e teríamos que escrever uma classe semelhante para todos os outros tipos de dados do mundo! Contaremos com nossos programadores e apenas deixaremos um comentário no código para garantir que eles não adicionem nada que não queremos:

// Use this class ONLY with the Integer data type
public void add(Object o) {
   this.data[count] = o;
   count++;
}
Um dos programadores perdeu este comentário e inadvertidamente colocou várias Strings em uma lista de números e então calculou sua soma:

public class Main {

   public static void main(String[] args) {

       MyListClass list = new MyListClass();
       list.add(100);
       list.add(200);
       list.add("Lolkek");
       list.add("Shalala");

       Integer sum1 = (Integer) list.getData()[0] + (Integer) list.getData()[1];
       System.out.println(sum1);

       Integer sum2 = (Integer) list.getData()[2] + (Integer) list.getData()[3];
       System.out.println(sum2);
   }
}
Saída do console:

300 
Exception in thread "main" java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Integer 
      at Main.main (Main.java:14)
Qual é a pior parte desta situação? Certamente não é o descuido do programador. A pior parte é que o código incorreto acabou em um lugar importante em nosso programa e foi compilado com sucesso. Agora, encontraremos o bug não enquanto escrevemos o código, mas apenas durante o teste (e este é o melhor cenário!). Corrigir bugs em estágios posteriores de desenvolvimento custa muito mais - tanto em termos de dinheiro quanto de tempo. É exatamente aqui que os genéricos nos beneficiam: uma classe genérica permite que o programador azarado detecte o erro imediatamente. O programa simplesmente não compila!

import java.util.ArrayList;
import java.util.List;

public class Main {

   public static void main(String[] args) {

       List<Integer> myList1 = new ArrayList<>();
      
       myList1.add(100);
       myList1.add(100);
       myList1.add ("Lolkek"); // Error!
       myList1.add("Shalala"); // Error!
   }
}
O programador imediatamente percebe seu erro e melhora instantaneamente. A propósito, não precisamos criar nossa própria classe List para ver esse tipo de erro. Simplesmente remova os colchetes angulares e digite ( <Integer> ) de um ArrayList comum!

import java.util.ArrayList;
import java.util.List;

public class Main {

   public static void main(String[] args) {

      List list = new ArrayList();

      list.add(100);
      list.add(200);
      list.add("Lolkek");
      list.add("Shalala");

       System.out.println((Integer) list.get(0) + (Integer) list.get(1));
       System.out.println((Integer) list.get(2) + (Integer) list.get(3));
   }
}
Saída do console:

300 
Exception in thread "main" java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Integer 
     at Main.main(Main.java:16)
Ou seja, mesmo usando os mecanismos "nativos" do Java, podemos cometer esse tipo de erro e criar uma coleção insegura. No entanto, se colarmos esse código em um IDE, receberemos um aviso: "Unchecked call to add(E) as a member of raw type of java.util.List" Somos informados de que algo pode dar errado ao adicionar um item para uma coleção que não possui um tipo genérico. Mas o que significa a expressão "tipo bruto"? Um tipo bruto é uma classe genérica cujo tipo foi removido. Em outras palavras, List myList1 é um tipo bruto . O oposto de um tipo bruto é um tipo genérico — uma classe genérica com uma indicação do (s) tipo(s) parametrizado(s) . Por exemplo, List<String> myList1. Você pode perguntar por que a linguagem permite o uso de tipos brutos ? A razão é simples. Os criadores do Java deixaram o suporte para tipos brutos na linguagem para evitar problemas de compatibilidade. Na época em que o Java 5.0 foi lançado (os genéricos apareceram pela primeira vez nesta versão), muito código já havia sido escrito usando tipos brutos . Como resultado, esse mecanismo ainda é suportado hoje. Mencionamos repetidamente o livro clássico de Joshua Bloch "Effective Java" nas lições. Como um dos criadores da linguagem, ele não ignorou tipos brutos e tipos genéricos em seu livro. O que são genéricos em Java?  - 2O capítulo 23 do livro tem um título muito eloqüente: "Não use tipos brutos no novo código" É disso que você precisa se lembrar. Ao usar classes genéricas, nunca transforme um tipo genérico em um tipo bruto .

métodos genéricos

Java permite parametrizar métodos individuais criando os chamados métodos genéricos. Como esses métodos são úteis? Acima de tudo, eles são úteis porque permitem que você trabalhe com diferentes tipos de parâmetros de método. Se a mesma lógica puder ser aplicada com segurança a diferentes tipos, um método genérico pode ser uma ótima solução. Considere este um exemplo muito simples: suponha que tenhamos uma lista chamada myList1 . Queremos remover todos os valores da lista e preencher todos os espaços vazios com novos valores. Aqui está a aparência da nossa classe com um método genérico:

public class TestClass {

   public static <T> void fill(List<T> list, T val) {
       for (int i = 0; i < list.size(); i++)
           list.set(i, val);
   }

   public static void main(String[] args) {

       List<String> strings = new ArrayList<>();
       strings.add("Old String 1");
       strings.add("Old String 2");
       strings.add("Old String 3");

       fill(strings, "New String");

       System.out.println(strings);

       List<Integer> numbers = new ArrayList<>();
       numbers.add(1);
       numbers.add(2);
       numbers.add(3);

       fill(numbers, 888);
       System.out.println(numbers);
   }
}
Preste atenção na sintaxe. Parece um pouco incomum:

public static <T> void fill(List<T> list, T val)
Escrevemos <T> antes do tipo de retorno. Isso indica que estamos lidando com um método genérico. Nesse caso, o método aceita 2 parâmetros como entrada: uma lista de objetos T e outro objeto T separado. Usando <T>, parametrizamos os tipos de parâmetro do método: não podemos passar uma lista de Strings e um Integer. Uma lista de Strings e uma String, uma lista de Integers e um Integer, uma lista de nossos próprios objetos Cat e outro objeto Cat — é isso que precisamos fazer. O método main() ilustra como o método fill() pode ser facilmente usado para trabalhar com diferentes tipos de dados. Primeiro, usamos o método com uma lista de Strings e uma String como entrada e, em seguida, com uma lista de Integers e um Integer. Saída do console:

[New String, New String, New String] [888, 888, 888]
Imagine se não tivéssemos métodos genéricos e precisássemos da lógica do método fill() para 30 classes diferentes. Teríamos que escrever o mesmo método 30 vezes para diferentes tipos de dados! Mas graças aos métodos genéricos, podemos reutilizar nosso código! :)

classes genéricas

Você não está limitado às classes genéricas fornecidas nas bibliotecas Java padrão — você pode criar as suas próprias! Aqui está um exemplo simples:

public class Box<T> {

   private T t;

   public void set(T t) {
       this.t = t;
   }

   public T get() {
       return t;
   }

   public static void main(String[] args) {

       Box<String> stringBox = new Box<>();

       stringBox.set("Old String");
       System.out.println(stringBox.get());
       stringBox.set("New String");

       System.out.println(stringBox.get());
      
       stringBox.set(12345); // Compilation error!
   }
}
Nossa classe Box<T> é uma classe genérica. Uma vez atribuído um tipo de dados ( <T> ) durante a criação, não podemos mais colocar objetos de outros tipos nele. Isso pode ser visto no exemplo. Ao criar nosso objeto, indicamos que funcionaria com Strings:

Box<String> stringBox = new Box<>();
E na última linha do código, quando tentamos colocar o número 12345 dentro da caixa, obtemos um erro de compilação! É tão fácil! Criamos nossa própria classe genérica! :) Com isso, a aula de hoje chega ao fim. Mas não estamos nos despedindo dos genéricos! Nas próximas lições, falaremos sobre recursos mais avançados, então não perca tempo! ) Para reforçar o que você aprendeu, sugerimos que você assista a uma vídeo aula do nosso Curso de Java
Muito sucesso em seus estudos! :)
Comentários
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION