O que é genérico em Java?

O Generics in Java foi introduzido em 2004 como um novo recurso da linguagem de programação Java e fazia parte do release JDK 5. É o mais amplamente usado junto com a estrutura de coleções Java. Atualmente, é um dos recursos mais importantes e procurados da linguagem de programação Java.

O Java genérico foi encontrado por quatro indivíduos: Gilad Bracha, Martin Odersky, David Stoutamire e Philip Wadler em 1998. Era uma extensão da linguagem Java que suportava tipos genéricos. O objetivo era atingir dois objetivos principais:

  1. Digite segurança
  2. Reutilização de código

Definição de genéricos em Java

Os genéricos podem ser definidos como uma maneira de obter a reutilização de código, definindo classes, interfaces, construtores e métodos genéricos que podem ser usados ​​com diferentes tipos de dados e também obter segurança de tipo, declarando o tipo de dados que está sendo usado na implementação com antecedência, eliminando assim as chances de um erro em tempo de execução.

Como os genéricos são implementados em Java?

Os genéricos são implementados usando colchetes angulares “”. Os colchetes incluem o parâmetro de tipo "T" dentro deles. Exemplo, . O parâmetro de tipo "T" é um marcador de posição que indica que um tipo de dado será atribuído a ele no tempo de execução. Por exemplo, uma classe genérica será definida como:

public class MyGenericClass (…)

A seguir estão os parâmetros de tipo padrão:

  • T: Tipo
  • E: Elemento
  • N: Número
  • K: Key
  • V: Valor

S, U, V e assim por diante são usados ​​para definir o segundo, o terceiro e o quarto parâmetros, respectivamente, no caso de o uso de multiparâmetros.

Compreendendo genéricos em Java

Até agora você deve estar se perguntando o que é segurança de tipo e como isso funciona? Ou como as classes, interfaces, construtores e métodos genéricos diferem de nossas classes e métodos regulares que os tornam reutilizáveis? Vamos descobrir.

Java sendo uma linguagem de tipo estaticamente requer que você declare o “tipo” que é o tipo de dados do valor que está sendo mantido pela variável antes de usá-lo.

Exemplo: String myString =”eduCBA”;

Aqui "String" é o tipo de dados, "myString" é a variável que conterá um valor cujo tipo é String.

Agora, se você tentar passar um valor booleano no lugar de uma sequência, por exemplo:

String myBooleanStr = true;

Você receberá imediatamente um erro em tempo de compilação informando "Tipo incompatível: não é possível converter de booleano para String".

Como alcançamos a reutilização de código com genéricos?

Agora, vamos definir um método regular:

public static void welcome(String name)(
System.out.println("welcome to " + name);
)

Este método pode ser chamado apenas passando um parâmetro de string. Por exemplo:

welcome(“eduCBA”);

Sua saída será "bem-vinda ao eduCBA".

No entanto, você não pode invocar esse método ignorando outros tipos de dados, como número inteiro ou booleano. Se você tentar fazer isso, será solicitado um erro em tempo de compilação informando "O método welcome (String) no tipo Runner não é aplicável aos argumentos (booleano)". Isso significa que você não pode passar nenhum outro tipo de dados para um método que aceite apenas uma string como parâmetro.

Isso também significa que, se você deseja chamar um método semelhante para um tipo de dados diferente, precisará escrever um novo método que aceite o tipo de dados necessário como parâmetro. Esse recurso de reescrever métodos com parâmetros de diferentes tipos de dados também é conhecido como sobrecarga de método. A principal desvantagem disso é que aumenta o tamanho do seu código.

No entanto, também poderíamos usar os Genéricos para reescrever o método acima e usá-lo para qualquer tipo de dados necessário.

Definindo um método genérico:

public static void welcome(T t)(
System.out.println("it is " + t);
)

Nota : Aqui "t" é um objeto do tipo T. Será atribuído o tipo de dados que está sendo usado para chamar o método.

Agora você pode reutilizar esse método invocando-o para uma cadeia de caracteres quando necessário ou um booleano ou um número inteiro ou qualquer outro tipo de dados.

welcome("educate");
Integer Myint = 1;
welcome(Myint)
welcome(true);

As instruções acima fornecerão a saída abaixo:

É Educa
É 1
Isso é verdade

Portanto, usando genéricos aqui, podemos reutilizar nosso método para diferentes tipos de dados.

Como alcançamos a segurança de tipos usando genéricos?

Uma das principais diferenças entre matrizes e coleção é que matrizes podem armazenar apenas dados homogêneos, enquanto coleções podem armazenar dados heterogêneos. Ou seja, Coleções pode armazenar qualquer tipo de dados / objetos definidos pelo usuário.

NOTA: As coleções podem conter apenas objetos (tipo de dados definido pelo usuário) e não um tipo de dados primitivo. Para trabalhar com dados primitivos, as coleções de tipos usam classes de wrapper.

Agora, vamos considerar um ArrayList.

ArrayList myList = new ArrayList();

Vamos adicionar dados do tipo String, Integer e Double ao objeto ArrayList.

myList.add("eduCBA");
myList.add(1);
myList.add(5.2);

Ao imprimir o objeto ArrayList, podemos ver que ele contém os seguintes valores: (eduCBA, 1, 5.2).

Agora, se você deseja recuperar esses valores em variáveis, precisará digitá-los.

String someStr = (String)myList.get(0);
Integer someInt = (Integer)myList.get(1);
Double someFlt = (Double)myList.get(2);

Caso você não faça a conversão, será solicitado um erro em tempo de compilação informando "Tipo incompatível: não é possível converter do objeto para a string".

A partir disso, você pode concluir que, ao recuperar os objetos da sua ArrayList, é necessário convertê-lo nos respectivos tipos. A questão que se coloca aqui é como você saberá qual tipo de dados deve ser digitado? Em tempo real, seu ArrayList conterá milhares de registros e a conversão para diferentes tipos de dados para cada objeto individual não será uma opção. Você pode acabar convertendo-o para o tipo de dados errado. O que acontece depois?

Dessa vez, você não receberá um erro de tempo de compilação, mas emitirá um erro de tempo de execução informando “Exceção no encadeamento“ main ”java.lang.ClassCastException: java.lang.Integer não pode ser convertido para java.lang.String em com.serviceClasess.Runner .main (Runner.java:43) ".

Como não podemos garantir o tipo de dados presentes em uma coleção (neste caso, ArrayList), eles são considerados não seguros para uso em relação ao tipo. É aqui que os genéricos entram em ação para fornecer segurança de tipo.

Usando ArrayList com genéricos:

ArrayList myList = new ArrayList();

Observe que entre colchetes angulares “”, o tipo String é especificado, o que significa que essa implementação específica de ArrayList pode conter apenas dados do tipo String. Se você tentar adicionar qualquer outro tipo de dados, ele simplesmente lançará um erro de tempo de compilação. Aqui você tornou seu ArrayList com segurança de tipo, eliminando a chance de adicionar um tipo de dados diferente de "String".

Agora que você especificou o tipo de dados que pode ser adicionado à sua coleção com a ajuda de genéricos, não é mais necessário convertê-lo durante a recuperação dos dados. Ou seja, você pode simplesmente recuperar seus dados escrevendo:

String someStr = myList.get(0);

Como o Generics em Java facilita o trabalho?

Isso ajuda a tornar suas coleções seguras para o tipo, garantindo assim que seu código não falhe posteriormente, devido a uma exceção de tempo de execução. Ele também evita que o codificador tenha que digitar todos os objetos da coleção, tornando o desenvolvimento do código mais rápido e fácil. Ao usar classes e métodos genéricos, também é possível reutilizar o código conforme o tipo de dados necessário durante a implementação.

O que mais você pode fazer com os genéricos em Java?

Até agora, vimos como podemos obter segurança de tipo e reutilização de código com genéricos. Agora, vejamos os outros recursos que os genéricos fornecem. Eles são:

  1. Limite e vários tipos delimitados
  2. Digite curingas

Tipo delimitado: no caso de um tipo delimitado, o tipo de dados de um parâmetro é delimitado para um intervalo específico. Isso é alcançado com a ajuda da palavra-chave "extends".

Por exemplo, vamos considerar uma classe genérica com um parâmetro de tipo limitado que estende a interface Runnable:

class myGenericClass()

Agora, ao criar seu objeto em outra classe:

myGenericClass myGen = new myGenericClass();

A declaração acima será executada perfeitamente, sem erros. No caso do tipo delimitado, você pode passar o mesmo tipo de classe ou seu tipo de classe filho. Além disso, você pode vincular o tipo de parâmetro a uma interface e transmitir suas implementações ao invocá-lo, como no caso do nosso exemplo acima.

O que acontece se você tentar usar outro tipo de parâmetro?

myGenericClass myGen = new myGenericClass();

No caso acima, você receberá um erro em tempo de compilação informando "Incompatibilidade vinculada: o tipo Inteiro não é um substituto válido para a conversão de tipo do tipo myGenericClass".

Vários tipos limitados: no caso de vários tipos limitados, podemos vincular o tipo de dados do parâmetro a mais de um tipo. Por exemplo,

Class myGeneric()

Nesse caso, você pode passar qualquer tipo que estenda a classe Number e implemente a interface Runnable. No entanto, ao usar vários tipos limitados, poucas coisas devem ser observadas:

  1. Não podemos estender mais de uma aula por vez.
  2. Podemos estender qualquer número de interfaces em um momento em que não haja limite para interfaces.
  3. O nome da classe sempre deve vir primeiro, seguido pelo nome da interface, caso contrário, isso resultará em um erro em tempo de compilação.

Caracteres curinga: são representados por "?" - símbolo de ponto de interrogação. Utiliza duas palavras-chave principais:

estende (para definir o limite superior) e super (para definir os limites inferiores).

Por exemplo,

ArrayList al

Este objeto ArrayList “al” conterá todos os dados do tipo T e todas as suas subclasses.

ArrayList al

Este objeto ArrayList “al” conterá todos os dados do tipo T e todas as suas superclasses.

Vantagens dos genéricos em Java

1. Flexibilidade : os genéricos fornecem ao nosso código a flexibilidade de acomodar diferentes tipos de dados com a ajuda de classes e métodos genéricos.

2. Manutenção e Reutilização do Código : Devido às classes e métodos genéricos, não é necessário reescrever o código, no caso de uma alteração nos requisitos posteriormente, tornando o código mais fácil de manter e reutilizar.

3. Segurança de tipo: fornece segurança de tipo à estrutura da coleção, definindo o tipo de dados que a coleção pode conter antecipadamente e eliminando qualquer chance de falha no tempo de execução devido ao ClassCastException.

4. Eliminando a necessidade de realizar a conversão de tipo: Como os tipos de dados mantidos pelas coleções já foram determinados, não é necessário fazer a conversão no momento da recuperação. Isso reduz o comprimento do código e também reduz o esforço de um codificador.

Genéricos em habilidades Java

Para trabalhar com Generics, você deve ser bem versado nos conceitos básicos de Java. Você deve entender como a verificação e a conversão de tipos funcionam. É necessário conhecimento profundo de outros conceitos, como sobrecarga de método, o relacionamento entre as classes pai e filho, as interfaces e suas implementações. Também é crucial entender a diferença entre tipos de dados primitivos (tipo de dados definido pelo sistema) e objetos (tipo de dados definido pelo usuário) quando se trata de trabalhar com a estrutura de coleta.

Por que devemos usar genéricos em Java?

O uso de genéricos torna nosso código mais sustentável, pois reduz a necessidade de reescrever o código específico do tipo de dados toda vez que há uma alteração nos requisitos. Ao usar o tipo limitado de genéricos, você pode restringir o tipo de dados e, ao mesmo tempo, fornecer flexibilidade ao seu código, definindo seu intervalo. É menos provável que seu código falhe posteriormente, pois fornece segurança de tipo, tornando-o menos propenso a erros.

Escopo para genéricos em Java

O escopo dos genéricos é limitado ao tempo de compilação. Isso significa que o conceito de genéricos é aplicável apenas no tempo de compilação, mas não no tempo de execução. Por exemplo,

ArrayList myList = new ArrayList();

ArrayList myList = new ArrayList();

ArrayList myList = new ArrayList();

ArrayList myList = new ArrayList();

Aqui todas as quatro instruções acima são uma e a mesma. Eles permitirão a adição de qualquer tipo de dados ao objeto de lista.

Conclusão

Os genéricos facilitam a codificação para um codificador. Isso diminui as chances de encontrar ClassCastException em tempo de execução, fornecendo forte verificação de tipo. Elimina completamente a necessidade de conversão de tipo, o que significa que menos código precisa ser gravado. Ele nos fornece a viabilidade de desenvolver algoritmos genéricos independentes do tipo de dados com os quais eles estão trabalhando.

Artigos recomendados

Este foi um guia para o que é genérico em Java ?. Aqui discutimos as habilidades, escopo, trabalho, compreensão e vantagem dos genéricos em Java. Você também pode consultar nossos outros artigos sugeridos para saber mais -

  1. O que é interface comum de gateway
  2. Como instalar o Java 8
  3. o que é soapUI
  4. O que é JavaScript?
  5. Booleanos Java