Java - Genéricos

Seria bom se pudéssemos escrever um único método de classificação que pudesse classificar os elementos em um array Integer, um array String ou um array de qualquer tipo que suporte ordenação.

Java Generic métodos e classes genéricas permitem que os programadores especifiquem, com uma única declaração de método, um conjunto de métodos relacionados, ou com uma única declaração de classe, um conjunto de tipos relacionados, respectivamente.

Os genéricos também fornecem segurança de tipo em tempo de compilação que permite aos programadores capturar tipos inválidos em tempo de compilação.

Usando o conceito Java Genérico, podemos escrever um método genérico para classificar uma matriz de objetos e, em seguida, invocar o método genérico com matrizes Integer, matrizes Double, matrizes String e assim por diante, para classificar os elementos da matriz.

Métodos Genéricos

Você pode escrever uma única declaração de método genérico que pode ser chamada com argumentos de diferentes tipos. Com base nos tipos de argumentos passados ​​para o método genérico, o compilador trata cada chamada de método apropriadamente. A seguir estão as regras para definir métodos genéricos -

  • Todas as declarações de método genérico têm uma seção de parâmetro de tipo delimitada por colchetes angulares (<e>) que precede o tipo de retorno do método (<E> no próximo exemplo).

  • Cada seção de parâmetro de tipo contém um ou mais parâmetros de tipo separados por vírgulas. Um parâmetro de tipo, também conhecido como variável de tipo, é um identificador que especifica um nome de tipo genérico.

  • Os parâmetros de tipo podem ser usados ​​para declarar o tipo de retorno e agir como espaços reservados para os tipos de argumentos passados ​​para o método genérico, que são conhecidos como argumentos de tipo reais.

  • O corpo de um método genérico é declarado como o de qualquer outro método. Observe que os parâmetros de tipo podem representar apenas tipos de referência, não tipos primitivos (como int, double e char).

Exemplo

O exemplo a seguir ilustra como podemos imprimir uma matriz de tipo diferente usando um único método genérico -

public class GenericMethodTest {
   // generic method printArray
   public static < E > void printArray( E[] inputArray ) {
      // Display array elements
      for(E element : inputArray) {
         System.out.printf("%s ", element);
      }
      System.out.println();
   }

   public static void main(String args[]) {
      // Create arrays of Integer, Double and Character
      Integer[] intArray = { 1, 2, 3, 4, 5 };
      Double[] doubleArray = { 1.1, 2.2, 3.3, 4.4 };
      Character[] charArray = { 'H', 'E', 'L', 'L', 'O' };

      System.out.println("Array integerArray contains:");
      printArray(intArray);   // pass an Integer array

      System.out.println("\nArray doubleArray contains:");
      printArray(doubleArray);   // pass a Double array

      System.out.println("\nArray characterArray contains:");
      printArray(charArray);   // pass a Character array
   }
}

Isso produzirá o seguinte resultado -

Resultado

Array integerArray contains:
1 2 3 4 5 

Array doubleArray contains:
1.1 2.2 3.3 4.4 

Array characterArray contains:
H E L L O

Parâmetros de tipo limitado

Pode haver momentos em que você queira restringir os tipos de tipos que podem ser passados ​​para um parâmetro de tipo. Por exemplo, um método que opera em números pode aceitar apenas instâncias de Number ou suas subclasses. É para isso que servem os parâmetros de tipo limitado.

Para declarar um parâmetro de tipo limitado, liste o nome do parâmetro de tipo, seguido pela palavra-chave extends, seguida por seu limite superior.

Exemplo

O exemplo a seguir ilustra como extends é usado em um sentido geral para significar "estende" (como nas classes) ou "implementa" (como nas interfaces). Este exemplo é o método genérico para retornar o maior dos três objetos comparáveis ​​-

public class MaximumTest {
   // determines the largest of three Comparable objects
   
   public static <T extends Comparable<T>> T maximum(T x, T y, T z) {
      T max = x;   // assume x is initially the largest
      
      if(y.compareTo(max) > 0) {
         max = y;   // y is the largest so far
      }
      
      if(z.compareTo(max) > 0) {
         max = z;   // z is the largest now                 
      }
      return max;   // returns the largest object   
   }
   
   public static void main(String args[]) {
      System.out.printf("Max of %d, %d and %d is %d\n\n", 
         3, 4, 5, maximum( 3, 4, 5 ));

      System.out.printf("Max of %.1f,%.1f and %.1f is %.1f\n\n",
         6.6, 8.8, 7.7, maximum( 6.6, 8.8, 7.7 ));

      System.out.printf("Max of %s, %s and %s is %s\n","pear",
         "apple", "orange", maximum("pear", "apple", "orange"));
   }
}

Isso produzirá o seguinte resultado -

Resultado

Max of 3, 4 and 5 is 5

Max of 6.6,8.8 and 7.7 is 8.8

Max of pear, apple and orange is pear

Classes Genéricas

Uma declaração de classe genérica parece uma declaração de classe não genérica, exceto que o nome da classe é seguido por uma seção de parâmetro de tipo.

Tal como acontece com os métodos genéricos, a seção de parâmetro de tipo de uma classe genérica pode ter um ou mais parâmetros de tipo separados por vírgulas. Essas classes são conhecidas como classes parametrizadas ou tipos parametrizados porque aceitam um ou mais parâmetros.

Exemplo

O exemplo a seguir ilustra como podemos definir uma classe genérica -

public class Box<T> {
   private T t;

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

   public T get() {
      return t;
   }

   public static void main(String[] args) {
      Box<Integer> integerBox = new Box<Integer>();
      Box<String> stringBox = new Box<String>();
    
      integerBox.add(new Integer(10));
      stringBox.add(new String("Hello World"));

      System.out.printf("Integer Value :%d\n\n", integerBox.get());
      System.out.printf("String Value :%s\n", stringBox.get());
   }
}

Isso produzirá o seguinte resultado -

Resultado

Integer Value :10
String Value :Hello World