Java - typy generyczne

Byłoby miło, gdybyśmy mogli napisać pojedynczą metodę sortowania, która mogłaby sortować elementy w tablicy Integer, tablicy String lub tablicy dowolnego typu, która obsługuje porządkowanie.

Jawa Generic metody i klasy generyczne umożliwiają programistom określanie, odpowiednio, za pomocą pojedynczej deklaracji metody, zestawu powiązanych metod lub za pomocą pojedynczej deklaracji klasy, zestawu powiązanych typów.

Typy generyczne zapewniają również bezpieczeństwo typu w czasie kompilacji, które umożliwia programistom wyłapywanie nieprawidłowych typów w czasie kompilacji.

Korzystając z koncepcji Java Generic, możemy napisać ogólną metodę sortowania tablicy obiektów, a następnie wywołać metodę ogólną z tablicami Integer, Double Arrays, String Arrays i tak dalej, aby posortować elementy tablicy.

Metody ogólne

Możesz napisać jedną ogólną deklarację metody, którą można wywołać z argumentami różnych typów. Na podstawie typów argumentów przekazanych do metody ogólnej kompilator odpowiednio obsługuje każde wywołanie metody. Poniżej przedstawiono zasady definiowania metod ogólnych -

  • Wszystkie deklaracje metod ogólnych mają sekcję parametru typu oddzieloną nawiasami ostrymi (<i>), która poprzedza zwracany typ metody (<E> w następnym przykładzie).

  • Każda sekcja parametru typu zawiera jeden lub więcej parametrów typu oddzielonych przecinkami. Parametr typu, znany również jako zmienna typu, jest identyfikatorem określającym nazwę typu ogólnego.

  • Parametry typu mogą służyć do deklarowania zwracanego typu i pełnić rolę symboli zastępczych dla typów argumentów przekazanych do metody ogólnej, które są znane jako argumenty typu rzeczywistego.

  • Treść metody ogólnej jest deklarowana tak, jak każda inna metoda. Należy zauważyć, że parametry typu mogą reprezentować tylko typy referencyjne, a nie typy pierwotne (takie jak int, double i char).

Przykład

Poniższy przykład ilustruje, jak możemy wydrukować tablicę innego typu przy użyciu jednej metody Generic -

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

To da następujący wynik -

Wynik

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

Ograniczone parametry typu

Może się zdarzyć, że będziesz chciał ograniczyć rodzaje typów, które mogą być przekazywane do parametru typu. Na przykład metoda działająca na liczbach może akceptować tylko wystąpienia klasy Number lub jej podklasy. Do tego służą parametry typu ograniczonego.

Aby zadeklarować parametr typu ograniczonego, należy podać nazwę parametru typu, po którym następuje słowo kluczowe extends, a następnie jego górna granica.

Przykład

Poniższy przykład ilustruje, w jaki sposób extends jest używane w ogólnym sensie w znaczeniu „rozszerza” (jak w klasach) lub „implementuje” (jak w interfejsach). Ten przykład jest metodą Generic zwracającą największy z trzech porównywalnych obiektów -

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

To da następujący wynik -

Wynik

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

Klasy ogólne

Deklaracja klasy ogólnej wygląda jak deklaracja klasy nieogólnej, z tą różnicą, że po nazwie klasy następuje sekcja parametru typu.

Podobnie jak w przypadku metod ogólnych, sekcja parametru typu klasy ogólnej może mieć jeden lub więcej parametrów typu oddzielonych przecinkami. Te klasy są znane jako sparametryzowane klasy lub sparametryzowane typy, ponieważ akceptują jeden lub więcej parametrów.

Przykład

Poniższy przykład ilustruje, jak możemy zdefiniować klasę ogólną -

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

To da następujący wynik -

Wynik

Integer Value :10
String Value :Hello World