Java - Generics

Sarebbe bello se potessimo scrivere un unico metodo di ordinamento in grado di ordinare gli elementi in un array Integer, in un array String o in un array di qualsiasi tipo che supporti l'ordinamento.

Giava Generic metodi e classi generiche consentono ai programmatori di specificare, rispettivamente con una singola dichiarazione di metodo, un insieme di metodi correlati o con una singola dichiarazione di classe, un insieme di tipi correlati.

I generics forniscono anche la protezione dai tipi in fase di compilazione che consente ai programmatori di rilevare i tipi non validi in fase di compilazione.

Utilizzando il concetto generico di Java, potremmo scrivere un metodo generico per ordinare un array di oggetti, quindi richiamare il metodo generico con array Integer, Double array, String array e così via, per ordinare gli elementi dell'array.

Metodi generici

È possibile scrivere una singola dichiarazione di metodo generico che può essere chiamata con argomenti di diversi tipi. In base ai tipi di argomenti passati al metodo generico, il compilatore gestisce ogni chiamata di metodo in modo appropriato. Di seguito sono riportate le regole per definire i metodi generici:

  • Tutte le dichiarazioni di metodi generici hanno una sezione di parametro di tipo delimitata da parentesi angolari (<e>) che precede il tipo restituito del metodo (<E> nell'esempio successivo).

  • Ogni sezione di parametro di tipo contiene uno o più parametri di tipo separati da virgole. Un parametro di tipo, noto anche come variabile di tipo, è un identificatore che specifica un nome di tipo generico.

  • I parametri di tipo possono essere utilizzati per dichiarare il tipo restituito e fungere da segnaposto per i tipi degli argomenti passati al metodo generico, noti come argomenti di tipo effettivo.

  • Il corpo di un metodo generico viene dichiarato come quello di qualsiasi altro metodo. Nota che i parametri di tipo possono rappresentare solo tipi di riferimento, non tipi primitivi (come int, double e char).

Esempio

L'esempio seguente illustra come è possibile stampare un array di tipo diverso utilizzando un unico metodo generico:

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
   }
}

Questo produrrà il seguente risultato:

Produzione

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

Parametri di tipo limitati

A volte potresti voler limitare i tipi di tipi che possono essere passati a un parametro di tipo. Ad esempio, un metodo che opera sui numeri potrebbe voler accettare solo istanze di Number o delle sue sottoclassi. Questo è ciò a cui servono i parametri di tipo limitato.

Per dichiarare un parametro di tipo limitato, elenca il nome del parametro di tipo, seguito dalla parola chiave extends, seguito dal suo limite superiore.

Esempio

L'esempio seguente illustra come extends sia usato in senso generale per significare "extends" (come nelle classi) o "implements" (come nelle interfacce). Questo esempio è il metodo generico per restituire il più grande dei tre oggetti comparabili:

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

Questo produrrà il seguente risultato:

Produzione

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

Classi generiche

Una dichiarazione di classe generica ha l'aspetto di una dichiarazione di classe non generica, tranne per il fatto che il nome della classe è seguito da una sezione di parametro di tipo.

Come con i metodi generici, la sezione dei parametri di tipo di una classe generica può avere uno o più parametri di tipo separati da virgole. Queste classi sono note come classi parametrizzate o tipi parametrizzati perché accettano uno o più parametri.

Esempio

L'esempio seguente illustra come possiamo definire una classe generica:

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

Questo produrrà il seguente risultato:

Produzione

Integer Value :10
String Value :Hello World