Java - Genéricos

Sería bueno si pudiéramos escribir un solo método de ordenación que pudiera ordenar los elementos en una matriz de enteros, una matriz de cadenas o una matriz de cualquier tipo que admita la ordenación.

Java Generic Los métodos y las clases genéricas permiten a los programadores especificar, con una única declaración de método, un conjunto de métodos relacionados, o con una única declaración de clase, un conjunto de tipos relacionados, respectivamente.

Los genéricos también proporcionan seguridad de tipos en tiempo de compilación que permite a los programadores detectar tipos no válidos en tiempo de compilación.

Usando el concepto genérico de Java, podríamos escribir un método genérico para ordenar una matriz de objetos, luego invocar el método genérico con matrices de enteros, matrices dobles, matrices de cadenas, etc.

Métodos genéricos

Puede escribir una única declaración de método genérico que se puede llamar con argumentos de diferentes tipos. Según los tipos de argumentos pasados ​​al método genérico, el compilador maneja cada llamada al método de manera apropiada. Las siguientes son las reglas para definir los métodos genéricos:

  • Todas las declaraciones de métodos genéricos tienen una sección de parámetro de tipo delimitada por corchetes angulares (<y>) que precede al tipo de retorno del método (<E> en el siguiente ejemplo).

  • Cada sección de parámetro de tipo contiene uno o más parámetros de tipo separados por comas. Un parámetro de tipo, también conocido como variable de tipo, es un identificador que especifica un nombre de tipo genérico.

  • Los parámetros de tipo se pueden usar para declarar el tipo de retorno y actuar como marcadores de posición para los tipos de argumentos pasados ​​al método genérico, que se conocen como argumentos de tipo real.

  • El cuerpo de un método genérico se declara como el de cualquier otro método. Tenga en cuenta que los parámetros de tipo solo pueden representar tipos de referencia, no tipos primitivos (como int, double y char).

Ejemplo

El siguiente ejemplo ilustra cómo podemos imprimir una matriz de diferentes tipos utilizando un ú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
   }
}

Esto producirá el siguiente resultado:

Salida

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 acotado

Puede haber ocasiones en las que desee restringir los tipos de tipos que se pueden pasar a un parámetro de tipo. Por ejemplo, es posible que un método que opera con números solo desee aceptar instancias de Number o sus subclases. Para esto son los parámetros de tipo acotado.

Para declarar un parámetro de tipo acotado, enumere el nombre del parámetro de tipo, seguido de la palabra clave extiende, seguido de su límite superior.

Ejemplo

El siguiente ejemplo ilustra cómo se usa extiende en un sentido general para significar "extiende" (como en las clases) o "implementa" (como en las interfaces). Este ejemplo es un método genérico para devolver el mayor de tres objetos comparables:

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"));
   }
}

Esto producirá el siguiente resultado:

Salida

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

Clases genéricas

Una declaración de clase genérica se ve como una declaración de clase no genérica, excepto que el nombre de la clase va seguido de una sección de parámetro de tipo.

Al igual que con los métodos genéricos, la sección de parámetros de tipo de una clase genérica puede tener uno o más parámetros de tipo separados por comas. Estas clases se conocen como clases parametrizadas o tipos parametrizados porque aceptan uno o más parámetros.

Ejemplo

El siguiente ejemplo ilustra cómo podemos definir una clase 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());
   }
}

Esto producirá el siguiente resultado:

Salida

Integer Value :10
String Value :Hello World