Generici Java - Guida rapida

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.

I metodi Java Generic e le 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.

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

Configurazione dell'ambiente locale

JUnit è un framework per Java, quindi il primo requisito è avere JDK installato sulla tua macchina.

Requisito del sistema

JDK 1.5 o superiore.
Memoria Nessun requisito minimo.
Spazio sul disco Nessun requisito minimo.
Sistema operativo Nessun requisito minimo.

Passaggio 1: verifica l'installazione di Java sulla macchina

Prima di tutto, apri la console ed esegui un comando java basato sul sistema operativo su cui stai lavorando.

OS Compito Comando
finestre Apri la Console di comando c: \> java -version
Linux Apri Terminale di comando $ java -version
Mac Apri Terminale macchina: <joseph $ java -version

Verifichiamo l'output per tutti i sistemi operativi -

OS Produzione
finestre

versione java "1.6.0_21"

Java (TM) SE Runtime Environment (build 1.6.0_21-b07)

VM client Java HotSpot (TM) (build 17.0-b17, modalità mista, condivisione)

Linux

versione java "1.6.0_21"

Java (TM) SE Runtime Environment (build 1.6.0_21-b07)

VM client Java HotSpot (TM) (build 17.0-b17, modalità mista, condivisione)

Mac

versione java "1.6.0_21"

Java (TM) SE Runtime Environment (build 1.6.0_21-b07)

VM server Java HotSpot (TM) a 64 bit (build 17.0-b17, modalità mista, condivisione)

Se non hai Java installato sul tuo sistema, scarica il Java Software Development Kit (SDK) dal seguente collegamento https://www.oracle.com. Stiamo assumendo Java 1.6.0_21 come versione installata per questo tutorial.

Passaggio 2: impostare l'ambiente JAVA

Impostare il JAVA_HOMEvariabile di ambiente in modo che punti alla posizione della directory di base in cui Java è installato sulla macchina. Per esempio.

OS Produzione
finestre Imposta la variabile d'ambiente JAVA_HOME su C: \ Program Files \ Java \ jdk1.6.0_21
Linux export JAVA_HOME = / usr / local / java-current
Mac export JAVA_HOME = / Library / Java / Home

Aggiungi la posizione del compilatore Java al percorso di sistema.

OS Produzione
finestre Aggiungi la stringa C:\Program Files\Java\jdk1.6.0_21\bin alla fine della variabile di sistema, Path.
Linux export PATH = $PATH:$JAVA_HOME / bin /
Mac non richiesto

Verifica l'installazione di Java utilizzando il comando java -version come spiegato sopra.

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

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.

Sintassi

public class Box<T> {
   private T t;
}

Dove

  • Box - Box è una classe generica.

  • T- Il parametro di tipo generico passato alla classe generica. Può accettare qualsiasi oggetto.

  • t - Istanza di tipo generico T.

Descrizione

La T è un parametro di tipo passato alla classe generica Box e deve essere passato quando viene creato un oggetto Box.

Esempio

Crea il seguente programma java utilizzando qualsiasi editor di tua scelta.

GenericsTester.java

package com.tutorialspoint;

public class GenericsTester {
   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", integerBox.get());
      System.out.printf("String Value :%s\n", stringBox.get());
   }
}

class Box<T> {
   private T t;

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

   public T get() {
      return t;
   }   
}

Questo produrrà il seguente risultato.

Produzione

Integer Value :10
String Value :Hello World

Per convenzione, i nomi dei parametri di tipo sono denominati come singole lettere maiuscole in modo che un parametro di tipo possa essere facilmente distinto con un nome di classe o interfaccia normale. Di seguito è riportato l'elenco dei nomi dei parametri di tipo comunemente utilizzati:

  • E - Element, ed è utilizzato principalmente dal framework Java Collections.

  • K - Key, e viene utilizzato principalmente per rappresentare il tipo di parametro della chiave di una mappa.

  • V - Value, e viene utilizzato principalmente per rappresentare il tipo di parametro di valore di una mappa.

  • N - Numero, e viene utilizzato principalmente per rappresentare i numeri.

  • T - Tipo, e viene utilizzato principalmente per rappresentare il primo parametro di tipo generico.

  • S - Tipo, e viene utilizzato principalmente per rappresentare il secondo parametro di tipo generico.

  • U - Tipo, e viene utilizzato principalmente per rappresentare il terzo parametro di tipo generico.

  • V - Tipo, e viene utilizzato principalmente per rappresentare il quarto parametro di tipo generico.

L'esempio seguente mostrerà il concetto sopra menzionato.

Esempio

Crea il seguente programma java utilizzando qualsiasi editor di tua scelta.

GenericsTester.java

package com.tutorialspoint;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class GenericsTester {
   public static void main(String[] args) {
      Box<Integer, String> box = new Box<Integer, String>();
      box.add(Integer.valueOf(10),"Hello World");
      System.out.printf("Integer Value :%d\n", box.getFirst());
      System.out.printf("String Value :%s\n", box.getSecond());

      Pair<String, Integer> pair = new Pair<String, Integer>(); 
      pair.addKeyValue("1", Integer.valueOf(10));
      System.out.printf("(Pair)Integer Value :%d\n", pair.getValue("1"));

      CustomList<Box> list = new CustomList<Box>();
      list.addItem(box);
      System.out.printf("(CustomList)Integer Value :%d\n", list.getItem(0).getFirst());
   }
}

class Box<T, S> {
   private T t;
   private S s;

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

   public T getFirst() {
      return t;
   } 

   public S getSecond() {
      return s;
   } 
}

class Pair<K,V>{
   private Map<K,V> map = new HashMap<K,V>();

   public void addKeyValue(K key, V value) {
      map.put(key, value);
   }

   public V getValue(K key) {
      return map.get(key);
   }
}

class CustomList<E>{
   private List<E> list = new ArrayList<E>();

   public void addItem(E value) {
      list.add(value);
   }

   public E getItem(int index) {
      return list.get(index);
   }
}

Questo produrrà il seguente risultato.

Produzione

Integer Value :10
String Value :Hello World
(Pair)Integer Value :10
(CustomList)Integer Value :10

L'inferenza del tipo rappresenta la capacità del compilatore Java di esaminare una chiamata di metodo e la sua dichiarazione corrispondente per controllare e determinare gli argomenti del tipo. L'algoritmo di inferenza controlla i tipi degli argomenti e, se disponibile, viene restituito il tipo assegnato. Gli algoritmi di inferenza cercano di trovare un tipo specifico che possa riempire tutti i parametri del tipo.

Il compilatore genera un avviso di conversione non controllato nel caso in cui l'inferenza del tipo non venga utilizzata.

Sintassi

Box<Integer> integerBox = new Box<>();

Dove

  • Box - Box è una classe generica.

  • <> - L'operatore diamante denota l'inferenza del tipo.

Descrizione

Utilizzando l'operatore rombo, il compilatore determina il tipo di parametro. Questo operatore è disponibile dalla versione Java SE 7 in poi.

Esempio

Crea il seguente programma java utilizzando qualsiasi editor di tua scelta.

GenericsTester.java

package com.tutorialspoint;

public class GenericsTester {
   public static void main(String[] args) {
      //type inference   
      Box<Integer> integerBox = new Box<>();
      //unchecked conversion warning
      Box<String> stringBox = new Box<String>();

      integerBox.add(new Integer(10));
      stringBox.add(new String("Hello World"));

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

class Box<T> {
   private T t;

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

   public T get() {
      return t;
   }   
}

Questo produrrà il seguente risultato.

Produzione

Integer Value :10
String Value :Hello World

È possibile scrivere una singola dichiarazione di metodo generico che può essere chiamata con argomenti di tipi diversi. 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 del 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. Si noti 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 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

Una classe generica può avere parametri di tipo multiplo. L'esempio seguente mostrerà il concetto sopra menzionato.

Esempio

Crea il seguente programma java utilizzando qualsiasi editor di tua scelta.

GenericsTester.java

package com.tutorialspoint;

public class GenericsTester {
   public static void main(String[] args) {
      Box<Integer, String> box = new Box<Integer, String>();
      box.add(Integer.valueOf(10),"Hello World");
      System.out.printf("Integer Value :%d\n", box.getFirst());
      System.out.printf("String Value :%s\n", box.getSecond());

      Box<String, String> box1 = new Box<String, String>();
      box1.add("Message","Hello World");
      System.out.printf("String Value :%s\n", box1.getFirst());
      System.out.printf("String Value :%s\n", box1.getSecond());
   }
}

class Box<T, S> {
   private T t;
   private S s;

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

   public T getFirst() {
      return t;
   } 

   public S getSecond() {
      return s;
   } 
}

Questo produrrà il seguente risultato.

Produzione

Integer Value :10
String Value :Hello World
String Value :Message
String Value :Hello World

Una classe generica può avere tipi parametrizzati in cui un parametro di tipo può essere sostituito con un tipo parametrizzato. L'esempio seguente mostrerà il concetto sopra menzionato.

Esempio

Crea il seguente programma java utilizzando qualsiasi editor di tua scelta.

GenericsTester.java

package com.tutorialspoint;

import java.util.ArrayList;
import java.util.List;


public class GenericsTester {
   public static void main(String[] args) {
      Box<Integer, List<String>> box
         = new Box<Integer, List<String>>();
      
      List<String> messages = new ArrayList<String>();
      
      messages.add("Hi");
      messages.add("Hello");
      messages.add("Bye");      
      
      box.add(Integer.valueOf(10),messages);
      System.out.printf("Integer Value :%d\n", box.getFirst());
      System.out.printf("String Value :%s\n", box.getSecond());

      
   }
}

class Box<T, S> {
   private T t;
   private S s;

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

   public T getFirst() {
      return t;
   } 

   public S getSecond() {
      return s;
   } 
}

Questo produrrà il seguente risultato.

Produzione

Integer Value :10
String Value :[Hi, Hello, Bye]

Un tipo grezzo è un oggetto di una classe o interfaccia generica se i suoi argomenti di tipo non vengono passati durante la sua creazione. L'esempio seguente mostrerà il concetto sopra menzionato.

Esempio

Crea il seguente programma java utilizzando qualsiasi editor di tua scelta.

GenericsTester.java

package com.tutorialspoint;

public class GenericsTester {
   public static void main(String[] args) {
      Box<Integer> box = new Box<Integer>();
      
      box.set(Integer.valueOf(10));
      System.out.printf("Integer Value :%d\n", box.getData());
      
      
      Box rawBox = new Box();
      
      //No warning
      rawBox = box;
      System.out.printf("Integer Value :%d\n", rawBox.getData());
      
      //Warning for unchecked invocation to set(T)
      rawBox.set(Integer.valueOf(10));
      System.out.printf("Integer Value :%d\n", rawBox.getData());
      
      //Warning for unchecked conversion
      box = rawBox;
      System.out.printf("Integer Value :%d\n", box.getData());
   }
}

class Box<T> {
   private T t; 

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

   public T getData() {
      return t;
   } 
}

Questo produrrà il seguente risultato.

Produzione

Integer Value :10
Integer Value :10
Integer Value :10
Integer Value :10

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

Un parametro di tipo può avere più limiti.

Sintassi

public static <T extends Number & Comparable<T>> T maximum(T x, T y, T z)

Dove

  • maximum - il massimo è un metodo generico.

  • T- Il parametro di tipo generico passato al metodo generico. Può accettare qualsiasi oggetto.

Descrizione

La T è un parametro di tipo passato alla classe generica Box e dovrebbe essere un sottotipo della classe Number e deve impiantare l'interfaccia Comparable. Nel caso in cui una classe venga passata come associata, dovrebbe essere passata prima dell'interfaccia altrimenti si verificherà un errore in fase di compilazione.

Esempio

Crea il seguente programma java utilizzando qualsiasi editor di tua scelta.

package com.tutorialspoint;

public class GenericsTester {
   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 ));
   }

   public static <T extends Number 
      & Comparable<T>> T maximum(T x, T y, T z) {
      T max = x;      
      if(y.compareTo(max) > 0) {
         max = y;   
      }

      if(z.compareTo(max) > 0) {
         max = z;                    
      }
      return max;      
   }

   // Compiler throws error in case of below declaration
   /* public static <T extends Comparable<T> 
      & Number> T maximum1(T x, T y, T z) {
      T max = x;      
      if(y.compareTo(max) > 0) {
         max = y;   
      }

      if(z.compareTo(max) > 0) {
         max = z;                    
      }
      return max;   
   }*/
}

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

Java ha fornito supporto generico nell'interfaccia List.

Sintassi

List<T> list = new ArrayList<T>();

Dove

  • list - oggetto dell'interfaccia List.

  • T - Il parametro di tipo generico passato durante la dichiarazione dell'elenco.

Descrizione

La T è un parametro di tipo passato all'interfaccia generica List e alla sua classe di implementazione ArrayList.

Esempio

Crea il seguente programma java utilizzando qualsiasi editor di tua scelta.

package com.tutorialspoint;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

public class GenericsTester {
   public static void main(String[] args) {

      List<Integer> integerList = new ArrayList<Integer>();
  
      integerList.add(Integer.valueOf(10));
      integerList.add(Integer.valueOf(11));

      List<String> stringList = new ArrayList<String>();
  
      stringList.add("Hello World");
      stringList.add("Hi World");
 

      System.out.printf("Integer Value :%d\n", integerList.get(0));
      System.out.printf("String Value :%s\n", stringList.get(0));

      for(Integer data: integerList) {
         System.out.printf("Integer Value :%d\n", data);
      }

      Iterator<String> stringIterator = stringList.iterator();

      while(stringIterator.hasNext()) {
         System.out.printf("String Value :%s\n", stringIterator.next());
      }
   }  
}

Questo produrrà il seguente risultato:

Produzione

Integer Value :10
String Value :Hello World
Integer Value :10
Integer Value :11
String Value :Hello World
String Value :Hi World

Java ha fornito supporto generico nell'interfaccia Set.

Sintassi

Set<T> set = new HashSet<T>();

Dove

  • set - oggetto di Set Interface.

  • T - Il parametro di tipo generico passato durante la dichiarazione di set.

Descrizione

La T è un parametro di tipo passato all'interfaccia generica Set e alla sua classe di implementazione HashSet.

Esempio

Crea il seguente programma java utilizzando qualsiasi editor di tua scelta.

package com.tutorialspoint;

import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;

public class GenericsTester {
   public static void main(String[] args) {

      Set<Integer> integerSet = new HashSet<Integer>();
  
      integerSet.add(Integer.valueOf(10));
      integerSet.add(Integer.valueOf(11));

      Set<String> stringSet = new HashSet<String>();
  
      stringSet.add("Hello World");
      stringSet.add("Hi World");
 

      for(Integer data: integerSet) {
         System.out.printf("Integer Value :%d\n", data);
      }

      Iterator<String> stringIterator = stringSet.iterator();

      while(stringIterator.hasNext()) {
         System.out.printf("String Value :%s\n", stringIterator.next());
      }
   }  
}

Questo produrrà il seguente risultato:

Produzione

Integer Value :10
Integer Value :11
String Value :Hello World
String Value :Hi World

Java ha fornito supporto generico nell'interfaccia Map.

Sintassi

Set<T> set = new HashSet<T>();

Dove

  • set - oggetto di Set Interface.

  • T - Il parametro di tipo generico passato durante la dichiarazione di set.

Descrizione

La T è un parametro di tipo passato all'interfaccia generica Set e alla sua classe di implementazione HashSet.

Esempio

Crea il seguente programma java utilizzando qualsiasi editor di tua scelta.

package com.tutorialspoint;

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

public class GenericsTester {
   public static void main(String[] args) {

      Map<Integer,Integer> integerMap 
         = new HashMap<Integer,Integer>();
  
      integerMap.put(1, 10);
      integerMap.put(2, 11);

      Map<String,String> stringMap = new HashMap<String,String>();
    
      stringMap.put("1", "Hello World");
      stringMap.put("2","Hi World");
 

      System.out.printf("Integer Value :%d\n", integerMap.get(1));
      System.out.printf("String Value :%s\n", stringMap.get("1"));

      // iterate keys.
      Iterator<Integer> integerIterator   = integerMap.keySet().iterator();

      while(integerIterator.hasNext()) {
         System.out.printf("Integer Value :%d\n", integerIterator.next());
      }

      // iterate values.
      Iterator<String> stringIterator   = stringMap.values().iterator();

      while(stringIterator.hasNext()) {
         System.out.printf("String Value :%s\n", stringIterator.next());
      }
   }  
}

Questo produrrà il seguente risultato:

Produzione

Integer Value :10
String Value :Hello World
Integer Value :1
Integer Value :2
String Value :Hello World
String Value :Hi World

Il punto interrogativo (?), Rappresenta il carattere jolly, sta per tipo sconosciuto nei generici. 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.

Per dichiarare un parametro Wildcard delimitato superiore, elenca il?, Seguito dalla parola chiave extends, seguito dal suo limite superiore.

Esempio

L'esempio seguente illustra come viene utilizzato extends per specificare un carattere jolly del limite superiore.

package com.tutorialspoint;

import java.util.Arrays;
import java.util.List;

public class GenericsTester {

   public static double sum(List<? extends Number> numberlist) {
      double sum = 0.0;
      for (Number n : numberlist) sum += n.doubleValue();
      return sum;
   }

   public static void main(String args[]) {
      List<Integer> integerList = Arrays.asList(1, 2, 3);
      System.out.println("sum = " + sum(integerList));

      List<Double> doubleList = Arrays.asList(1.2, 2.3, 3.5);
      System.out.println("sum = " + sum(doubleList));
   }
}

Questo produrrà il seguente risultato:

Produzione

sum = 6.0
sum = 7.0

Il punto interrogativo (?), Rappresenta il carattere jolly, sta per tipo sconosciuto nei generici. Ci possono essere momenti in cui qualsiasi oggetto può essere utilizzato quando un metodo può essere implementato utilizzando la funzionalità fornita nella classe Object o Quando il codice è indipendente dal parametro di tipo.

Per dichiarare un parametro Unbounded Wildcard, elenca il? solo.

Esempio

L'esempio seguente illustra come viene utilizzato extends per specificare un carattere jolly illimitato.

package com.tutorialspoint;

import java.util.Arrays;
import java.util.List;

public class GenericsTester {
   public static void printAll(List<?> list) {
      for (Object item : list)
         System.out.println(item + " ");
   }

   public static void main(String args[]) {
      List<Integer> integerList = Arrays.asList(1, 2, 3);
      printAll(integerList);
      List<Double> doubleList = Arrays.asList(1.2, 2.3, 3.5);
      printAll(doubleList);
   }
}

Questo produrrà il seguente risultato:

Produzione

1 
2 
3 
1.2 
2.3 
3.5

Il punto interrogativo (?), Rappresenta il carattere jolly, sta per tipo sconosciuto nei generici. 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 Integer o delle sue superclassi come Number.

Per dichiarare un parametro Wildcard con limite inferiore, elenca il?, Seguito dalla parola chiave super, seguito dal suo limite inferiore.

Esempio

L'esempio seguente illustra come viene utilizzato super per specificare un carattere jolly con limite inferiore.

package com.tutorialspoint;

import java.util.ArrayList;
import java.util.List;

public class GenericsTester {

   public static void addCat(List<? super Cat> catList) {
      catList.add(new RedCat());
      System.out.println("Cat Added");
   }

   public static void main(String[] args) {
      List<Animal> animalList= new ArrayList<Animal>();
      List<Cat> catList= new ArrayList<Cat>();
      List<RedCat> redCatList= new ArrayList<RedCat>();
      List<Dog> dogList= new ArrayList<Dog>();

      //add list of super class Animal of Cat class
      addCat(animalList);

      //add list of Cat class
      addCat(catList);

      //compile time error
      //can not add list of subclass RedCat of Cat class
      //addCat(redCatList);

      //compile time error
      //can not add list of subclass Dog of Superclass Animal of Cat class
      //addCat.addMethod(dogList); 
   }
}
class Animal {}

class Cat extends Animal {}

class RedCat extends Cat {}

class Dog extends Animal {}

Questo produrrà il seguente risultato:

Cat Added
Cat Added

I caratteri jolly possono essere utilizzati in tre modi:

  • Upper bound Wildcard-? estende Type.

  • Lower bound Wildcard-? super tipo.

  • Unbounded Wildcard -?

Per decidere quale tipo di carattere jolly si adatta meglio alla condizione, classifichiamo prima il tipo di parametri passati a un metodo come in e out parametro.

  • in variable- Una variabile in fornisce dati al codice. Ad esempio, copy (src, dest). Qui src agisce come una variabile che è un dato da copiare.

  • out variable- Una variabile out contiene i dati aggiornati dal codice. Ad esempio, copy (src, dest). Qui dest agisce come una variabile che ha copiato i dati.

Linee guida per i caratteri jolly.

  • Upper bound wildcard - Se una variabile è di in categoria, utilizza la parola chiave extends con carattere jolly.

  • Lower bound wildcard - Se una variabile è di out categoria, utilizza la parola chiave super con carattere jolly.

  • Unbounded wildcard - Se è possibile accedere a una variabile utilizzando il metodo della classe Object, utilizzare un carattere jolly non associato.

  • No wildcard - Se il codice sta accedendo a una variabile in entrambi in e out categoria quindi non utilizzare caratteri jolly.

Esempio

L'esempio seguente illustra i concetti sopra menzionati.

package com.tutorialspoint;

import java.util.ArrayList;
import java.util.List;

public class GenericsTester {

   //Upper bound wildcard
   //in category
   public static void deleteCat(List<? extends Cat> catList, Cat cat) {
      catList.remove(cat);
      System.out.println("Cat Removed");
   }

   //Lower bound wildcard
   //out category
   public static void addCat(List<? super RedCat> catList) {
      catList.add(new RedCat("Red Cat"));
      System.out.println("Cat Added");
   }

   //Unbounded wildcard
   //Using Object method toString()
   public static void printAll(List<?> list) {
      for (Object item : list)
         System.out.println(item + " ");
   }

   public static void main(String[] args) {

      List<Animal> animalList= new ArrayList<Animal>();
      List<RedCat> redCatList= new ArrayList<RedCat>();

      //add list of super class Animal of Cat class
      addCat(animalList);
      //add list of Cat class
      addCat(redCatList);  
      addCat(redCatList);  

      //print all animals
      printAll(animalList);
      printAll(redCatList);

      Cat cat = redCatList.get(0);
      //delete cat
      deleteCat(redCatList, cat);
      printAll(redCatList); 
   }
}

class Animal {
   String name;
   Animal(String name) { 
      this.name = name;
   }
   public String toString() { 
      return name;
   }
}

class Cat extends Animal { 
   Cat(String name) {
      super(name);
   }
}

class RedCat extends Cat {
   RedCat(String name) {
      super(name);
   }
}

class Dog extends Animal {
   Dog(String name) {
      super(name);
   }
}

Questo produrrà il seguente risultato:

Cat Added
Cat Added
Cat Added
Red Cat 
Red Cat 
Red Cat 
Cat Removed
Red Cat

I generici vengono utilizzati per controlli di tipo più rigorosi in fase di compilazione e per fornire una programmazione generica. Per implementare un comportamento generico, il compilatore Java applica la cancellazione del tipo. La cancellazione del tipo è un processo in cui il compilatore sostituisce un parametro generico con una classe o un metodo bridge effettivi. Nella cancellazione del tipo, il compilatore garantisce che non vengano create classi aggiuntive e che non vi sia alcun sovraccarico di runtime.

Digita Regole di cancellazione

  • Sostituisci i parametri di tipo nel tipo generico con il loro limite se vengono usati parametri di tipo limitato.

  • Sostituisci i parametri di tipo nel tipo generico con Object se vengono utilizzati parametri di tipo illimitato.

  • Inserire cast di tipo per preservare l'indipendenza dai tipi.

  • Genera metodi bridge per mantenere il polimorfismo in tipi generici estesi.

Il compilatore Java sostituisce i parametri di tipo nel tipo generico con il loro limite se vengono utilizzati parametri di tipo limitato.

Esempio

package com.tutorialspoint;

public class GenericsTester {
   public static void main(String[] args) {
      Box<Integer> integerBox = new Box<Integer>();
      Box<Double> doubleBox = new Box<Double>();

      integerBox.add(new Integer(10));
      doubleBox.add(new Double(10.0));

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

class Box<T extends Number> {
   private T t;

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

   public T get() {
      return t;
   }   
}

In questo caso, il compilatore java sostituirà T con la classe Number e dopo la cancellazione del tipo, il compilatore genererà bytecode per il codice seguente.

package com.tutorialspoint;

public class GenericsTester {
   public static void main(String[] args) {
      Box integerBox = new Box();
      Box doubleBox = new Box();

      integerBox.add(new Integer(10));
      doubleBox.add(new Double(10.0));

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

class Box {
   private Number t;

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

   public Number get() {
      return t;
   }   
}

In entrambi i casi, il risultato è lo stesso -

Produzione

Integer Value :10
Double Value :10.0

Java Compiler sostituisce i parametri di tipo nel tipo generico con Object se vengono utilizzati parametri di tipo illimitato.

Esempio

package com.tutorialspoint;

public class GenericsTester {
   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", integerBox.get());
      System.out.printf("String Value :%s\n", stringBox.get());
   }
}

class Box<T> {
   private T t;

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

   public T get() {
      return t;
   }   
}

In questo caso, il compilatore java sostituirà T con la classe Object e dopo la cancellazione del tipo, il compilatore genererà bytecode per il codice seguente.

package com.tutorialspoint;

public class GenericsTester {
   public static void main(String[] args) {
      Box integerBox = new Box();
      Box stringBox = new Box();

      integerBox.add(new Integer(10));
      stringBox.add(new String("Hello World"));

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

class Box {
   private Object t;

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

   public Object get() {
      return t;
   }   
}

In entrambi i casi, il risultato è lo stesso -

Produzione

Integer Value :10
String Value :Hello World

Java Compiler sostituisce i parametri di tipo nel tipo generico con Object se vengono utilizzati parametri di tipo illimitato e con type se vengono utilizzati parametri associati come parametri del metodo.

Esempio

package com.tutorialspoint;

public class GenericsTester {
   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"));
      
      printBox(integerBox);
      printBox1(stringBox);
   }
   
   private static <T extends Box> void printBox(T box) {
      System.out.println("Integer Value :" + box.get());
   }
   
   private static <T> void printBox1(T box) {
      System.out.println("String Value :" + ((Box)box).get());
   }
}

class Box<T> {
   private T t;

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

   public T get() {
      return t;
   }   
}

In questo caso, il compilatore java sostituirà T con la classe Object e dopo la cancellazione del tipo, il compilatore genererà bytecode per il codice seguente.

package com.tutorialspoint;

public class GenericsTester {
   public static void main(String[] args) {
      Box integerBox = new Box();
      Box stringBox = new Box();

      integerBox.add(new Integer(10));
      stringBox.add(new String("Hello World"));
      
      printBox(integerBox);
      printBox1(stringBox);
   }
	
   //Bounded Types Erasure
   private static void printBox(Box box) {
      System.out.println("Integer Value :" + box.get());
   }
	
   //Unbounded Types Erasure
   private static void printBox1(Object box) {
      System.out.println("String Value :" + ((Box)box).get());
   }
}

class Box {
   private Object t;

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

   public Object get() {
      return t;
   }   
}

In entrambi i casi, il risultato è lo stesso -

Produzione

Integer Value :10
String Value :Hello World

Utilizzando generics, i tipi primitivi non possono essere passati come parametri di tipo. Nell'esempio fornito di seguito, se passiamo il tipo primitivo int alla classe box, il compilatore si lamenterà. Per mitigare lo stesso, dobbiamo passare l'oggetto Integer invece del tipo primitivo int.

Esempio

package com.tutorialspoint;

public class GenericsTester {
   public static void main(String[] args) {
      Box<Integer> integerBox = new Box<Integer>();

      //compiler errror
      //ReferenceType
      //- Syntax error, insert "Dimensions" to complete
      ReferenceType
      //Box<int> stringBox = new Box<int>();

      integerBox.add(new Integer(10));
      printBox(integerBox);
   }

   private static void printBox(Box box) {
      System.out.println("Value: " + box.get());
   }  
}

class Box<T> {
   private T t;

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

   public T get() {
      return t;
   }   
}

Questo produrrà il seguente risultato:

Produzione

Value: 10

Un parametro di tipo non può essere utilizzato per istanziare il suo oggetto all'interno di un metodo.

public static <T> void add(Box<T> box) {
   //compiler error
   //Cannot instantiate the type T
   //T item = new T();  
   //box.add(item);
}

Per ottenere tale funzionalità, utilizzare la riflessione.

public static <T> void add(Box<T> box, Class<T> clazz) 
   throws InstantiationException, IllegalAccessException{
   T item = clazz.newInstance();   // OK
   box.add(item);
   System.out.println("Item added.");
}

Esempio

package com.tutorialspoint;

public class GenericsTester {
   public static void main(String[] args) 
      throws InstantiationException, IllegalAccessException {
      Box<String> stringBox = new Box<String>();
      add(stringBox, String.class);
   }  

   public static <T> void add(Box<T> box) {
      //compiler error
      //Cannot instantiate the type T
      //T item = new T();  
      //box.add(item);
   }

   public static <T> void add(Box<T> box, Class<T> clazz) 
      throws InstantiationException, IllegalAccessException{
      T item = clazz.newInstance();   // OK
      box.add(item);
      System.out.println("Item added.");
   }   
}

class Box<T> {
   private T t;

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

   public T get() {
      return t;
   }   
}

Questo produrrà il seguente risultato:

Item added.

Utilizzando generics, i parametri di tipo non possono essere statici. Poiché la variabile statica è condivisa tra gli oggetti, il compilatore non può determinare quale tipo utilizzare. Considera il seguente esempio se i parametri di tipo statico erano consentiti.

Esempio

package com.tutorialspoint;

public class GenericsTester {
   public static void main(String[] args) {
      Box<Integer> integerBox = new Box<Integer>();
	  Box<String> stringBox = new Box<String>();
	  
      integerBox.add(new Integer(10));
      printBox(integerBox);
   }

   private static void printBox(Box box) {
      System.out.println("Value: " + box.get());
   }  
}

class Box<T> {
   //compiler error
   private static T t;

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

   public T get() {
      return t;
   }   
}

Poiché sia ​​stringBox che integerBox hanno una variabile di tipo statico fissata, non è possibile determinarne il tipo. Quindi i parametri di tipo statico non sono consentiti.

Il cast in un tipo parametrizzato non è consentito a meno che non sia parametrizzato da caratteri jolly illimitati.

Box<Integer> integerBox = new Box<Integer>();
Box<Number> numberBox = new Box<Number>();
//Compiler Error: Cannot cast from Box<Number> to Box<Integer>
integerBox = (Box<Integer>)numberBox;

Per ottenere lo stesso risultato, è possibile utilizzare caratteri jolly illimitati.

private static void add(Box<?> box) {
   Box<Integer> integerBox = (Box<Integer>)box;
}

Poiché il compilatore utilizza la cancellazione del tipo, il runtime non tiene traccia dei parametri del tipo, quindi in fase di runtime la differenza tra Box <Integer> e Box <String> non può essere verificata utilizzando l'operatore instanceOf.

Box<Integer> integerBox = new Box<Integer>();

//Compiler Error:
//Cannot perform instanceof check against 
//parameterized type Box<Integer>. 
//Use the form Box<?> instead since further 
//generic type information will be erased at runtime
if(integerBox instanceof Box<Integer>) { }

Non sono consentiti array di tipi parametrizzati.

//Cannot create a generic array of Box<Integer>
Box<Integer>[] arrayOfLists = new Box<Integer>[2];

Poiché il compilatore utilizza la cancellazione del tipo, il parametro del tipo viene sostituito con Object e l'utente può aggiungere qualsiasi tipo di oggetto all'array. E in fase di esecuzione, il codice non sarà in grado di generare ArrayStoreException.

// compiler error, but if it is allowed
Object[] stringBoxes = new Box<String>[];
  
// OK
stringBoxes[0] = new Box<String>();  

// An ArrayStoreException should be thrown,
//but the runtime can't detect it.
stringBoxes[1] = new Box<Integer>();

A una classe generica non è consentito estendere direttamente o indirettamente la classe Throwable.

//The generic class Box<T> may not subclass java.lang.Throwable
class Box<T> extends Exception {}

//The generic class Box<T> may not subclass java.lang.Throwable
class Box1<T> extends Throwable {}

Un metodo non è autorizzato a catturare un'istanza di un parametro di tipo.

public static <T extends Exception, J> 
   void execute(List<J> jobs) {
      try {
         for (J job : jobs) {}
  
         // compile-time error
         //Cannot use the type parameter T in a catch block
      } catch (T e) { 
         // ...
   }
}

I parametri di tipo sono consentiti in una clausola throws.

class Box<T extends Exception>  {
   private int t;

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

   public int get() {
      return t;
   }   
}

Una classe non può avere due metodi sovraccaricati che possono avere la stessa firma dopo la cancellazione del tipo.

class Box  {
   //Compiler error
   //Erasure of method print(List<String>) 
   //is the same as another method in type Box
   public void print(List<String> stringList) { }
   public void print(List<Integer> integerList) { }
}