JavaRush /Blogue Java /Random-PT /O que são genéricos em Java

O que são genéricos em Java

Publicado no grupo Random-PT
Olá! Hoje falaremos sobre genéricos. Devo dizer que você aprenderá muitas coisas novas! Não só isso, mas também as próximas palestras serão dedicadas aos genéricos. O que são genéricos em Java - 1 Portanto, se este tema é do seu interesse, você tem sorte: hoje você aprenderá muito sobre as características dos genéricos. Bem, se não, acalme-se e relaxe! :) Este é um tema muito importante e você precisa conhecê-lo. Vamos começar com um simples: “o quê” e “por quê”. O que são genéricos? Genéricos são tipos com um parâmetro. Ao criar um genérico, você especifica não apenas seu tipo, mas também o tipo de dados com os quais ele deve trabalhar. Acho que o exemplo mais óbvio já veio à sua mente - este é ArrayList! Veja como normalmente o criamos no 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, a peculiaridade da lista é que não será possível “enfiar” tudo nela: ela funciona exclusivamente com objetos String. Agora vamos fazer uma breve excursão pela história do Java e tentar responder à pergunta: “por quê?” Para fazer isso, nós mesmos escreveremos uma versão simplificada da classe ArrayList. Nossa lista só pode adicionar dados ao array interno e receber estes dados:
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;
   }
}
Digamos que queremos que nossa lista armazene apenas números Integer. Não temos genéricos. Não podemos especificar explicitamente a instância o de check Integerno arquivo add(). Então toda a nossa classe será adequada apenas para Integer, e teremos que escrever a mesma classe para todos os tipos de dados existentes no mundo! Decidimos contar com nossos programadores e simplesmente deixar um comentário no código para que não acrescentem nada desnecessário:
//use it ONLY with Integer data type
public void add(Object o) {
   this.data[count] = o;
   count++;
}
Um dos programadores não percebeu este comentário e inadvertidamente tentou colocar números misturados com strings na lista e depois calcular 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 Exceção no thread "main" java.lang.ClassCastException: java.lang.String não pode ser convertido em java.lang.Integer em Main.main(Main.java:14) Qual é o pior nesta situação? Longe de ser a desatenção de um programador. O pior é que o código errado acabou em um lugar importante do nosso programa e foi compilado com sucesso . Agora veremos o erro não na fase de codificação, mas apenas na fase de teste (e isso é na melhor das hipóteses!). Corrigir bugs posteriormente no desenvolvimento custa muito mais – tanto dinheiro quanto tempo. Esta é precisamente a vantagem dos genéricos: uma classe genérica permitirá que um programador azarado detecte um erro imediatamente. O código 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 “cairá em si” e se corrigirá instantaneamente. Aliás, não precisamos criar nossa própria classe Listpara ver esse tipo de erro. Simplesmente remova os colchetes de tipo ( <Integer>) de um ArrayList normal!
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 Exceção no thread "main" java.lang.ClassCastException: java.lang.String não pode ser convertido para java.lang.Integer em Main.main(Main.java:16) Ou seja, mesmo usando ferramentas “nativas” Java, você pode cometer esse erro e criar uma coleção insegura. No entanto, se colarmos este código no IDEa, veremos um aviso: “ Chamada não verificada para add(E) como membro do tipo bruto de java.util.List ” Isso nos diz que algo pode dar errado ao adicionar um elemento a um coleção sem genéricos não é assim. Mas o que significa a frase “tipo bruto”? A tradução literal será bastante precisa - “ tipo bruto ” ou “ tipo sujo ”. Raw typeé uma classe genérica da qual seu tipo foi removido. Em outras palavras, List myList1isso é Raw type. O oposto raw typeé generic typeuma classe genérica (também conhecida como class parameterized type), criada corretamente, com uma especificação de tipo. Por exemplo, List<String> myList1. Você pode ter uma pergunta: por que é permitido usar raw types? A razão é simples. Os criadores do Java deixaram o suporte na linguagem raw typespara não criar problemas de compatibilidade. Quando 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 raw types. Portanto, essa possibilidade ainda existe hoje. Já mencionamos o livro clássico de Joshua Bloch, “Java Eficaz”, mais de uma vez em palestras. Como um dos criadores da linguagem, ele não ignorou o tema do uso raw typese no livro generic types. O que são genéricos em Java - 2O capítulo 23 deste livro tem um título muito eloqüente: “Não use tipos brutos em código novo”. Isso é algo que você precisa lembrar. Ao usar classes genéricas, nunca as transforme generic typeem arquivos raw type.

Métodos digitados

Java permite digitar métodos individuais, criando os chamados métodos genéricos. Por que esses métodos são convenientes? Em primeiro lugar, porque permitem trabalhar com diversos tipos de parâmetros. Se a mesma lógica puder ser aplicada com segurança a tipos diferentes, um método genérico será uma ótima solução. Vejamos um exemplo. Digamos que temos algum tipo de lista myList1. Queremos remover todos os valores dele e preencher todos os espaços livres com um novo valor. Esta é a aparência de 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("Старая строка 1");
       strings.add("Старая строка 2");
       strings.add("Старая строка 3");

       fill(strings, "Новая строка");

       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)
O tipo de retorno é precedido por <T>, que indica um método genérico. Neste caso, o método recebe 2 parâmetros como entrada: uma lista de objetos T e outro objeto separado T. Ao usar <T>, a digitação do método é alcançada: não podemos passar ali uma lista de strings e um número. Uma lista de strings e uma string, uma lista de números e um número, uma lista de nossos objetos Cate outro objeto Cat- essa é a única maneira. O método main()demonstra claramente que fill()funciona facilmente com diferentes tipos de dados. Primeiro, ele recebe como entrada uma lista de strings e uma string e, em seguida, uma lista de números e um número. Saída do console: [Newline, Newline, Newline] [888, 888, 888] Imagine se fill()precisássemos de lógica de método para 30 classes diferentes e não tivéssemos métodos genéricos. Seríamos forçados a escrever o mesmo método 30 vezes, apenas para tipos de dados diferentes! Mas graças aos métodos genéricos, podemos reutilizar o nosso código! :)

Aulas digitadas

Você pode não apenas usar as classes genéricas fornecidas em Java, mas também 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("Старая строка");
       System.out.println(stringBox.get());
       stringBox.set("Новая строка");

       System.out.println(stringBox.get());

       stringBox.set(12345);//ошибка компиляции!
   }
}
Nossa classe Box<T>(“caixa”) está digitada. Tendo atribuído um tipo de dados ( ) a ele durante a criação <T>, não poderemos mais colocar objetos de outros tipos nele. Isso pode ser visto no exemplo. Ao criar, especificamos que nosso objeto funcionará com strings:
Box<String> stringBox = new Box<>();
E quando na última linha do código tentamos colocar o número 12345 dentro da caixa, obtemos um erro de compilação! Simples assim, criamos nossa própria classe genérica! :) Isso conclui nossa palestra de hoje. Mas não estamos dizendo adeus aos genéricos! Nas próximas palestras falaremos sobre recursos mais avançados, então não diga adeus! ) Boa sorte em seus estudos! :)
Comentários
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION