Учебники

Java Generics — Краткое руководство

Java Generics — Обзор

Было бы хорошо, если бы мы могли написать единственный метод сортировки, который мог бы сортировать элементы в массиве Integer, массиве String или массиве любого типа, который поддерживает порядок.

Java Универсальные методы и универсальные классы позволяют программистам указывать с помощью одного объявления метода набор связанных методов или с помощью одного объявления класса набор связанных типов соответственно.

Обобщения также обеспечивают безопасность типов во время компиляции, что позволяет программистам перехватывать недопустимые типы во время компиляции.

Используя концепцию Java Generic, мы могли бы написать универсальный метод для сортировки массива объектов, а затем вызвать универсальный метод с массивами Integer, Double-массивами, массивами String и т. Д. Для сортировки элементов массива.

Java Generics — Настройка среды

Настройка локальной среды

JUnit — это фреймворк для Java, поэтому самое первое требование — установить JDK на вашем компьютере.

Системные требования

JDK 1,5 или выше.
объем памяти Нет минимальных требований.
Дисковое пространство Нет минимальных требований.
Операционная система Нет минимальных требований.

Шаг 1: Проверьте установку Java на вашем компьютере

Прежде всего, откройте консоль и выполните команду Java в зависимости от операционной системы, с которой вы работаете.

Операционные системы задача команда
Windows Открытая командная консоль c: \> Java-версия
Linux Открытый командный терминал $ java-версия
макинтош Открытый терминал машина: <joseph $ java -version

Давайте проверим вывод для всех операционных систем —

Операционные системы Выход
Windows

Java-версия «1.6.0_21»

Java (TM) SE Runtime Environment (сборка 1.6.0_21-b07)

Клиентская виртуальная машина Java HotSpot (TM) (сборка 17.0-b17, смешанный режим, совместное использование)

Linux

Java-версия «1.6.0_21»

Java (TM) SE Runtime Environment (сборка 1.6.0_21-b07)

Клиентская виртуальная машина Java HotSpot (TM) (сборка 17.0-b17, смешанный режим, совместное использование)

макинтош

Java-версия «1.6.0_21»

Java (TM) SE Runtime Environment (сборка 1.6.0_21-b07)

Java HotSpot (TM) 64-разрядная серверная виртуальная машина (сборка 17.0-b17, смешанный режим, совместное использование)

Java-версия «1.6.0_21»

Java (TM) SE Runtime Environment (сборка 1.6.0_21-b07)

Клиентская виртуальная машина Java HotSpot (TM) (сборка 17.0-b17, смешанный режим, совместное использование)

Java-версия «1.6.0_21»

Java (TM) SE Runtime Environment (сборка 1.6.0_21-b07)

Клиентская виртуальная машина Java HotSpot (TM) (сборка 17.0-b17, смешанный режим, совместное использование)

Java-версия «1.6.0_21»

Java (TM) SE Runtime Environment (сборка 1.6.0_21-b07)

Java HotSpot (TM) 64-разрядная серверная виртуальная машина (сборка 17.0-b17, смешанный режим, совместное использование)

Если у вас не установлена ​​Java в вашей системе, загрузите Java Software Development Kit (SDK) по следующей ссылке https://www.oracle.com . Мы предполагаем Java 1.6.0_21 в качестве установленной версии для этого урока.

Шаг 2: Установите среду JAVA

Установите переменную среды JAVA_HOME, чтобы она указывала на местоположение базовой директории, где установлена ​​Java на вашем компьютере. Например.

Операционные системы Выход
Windows Задайте для переменной среды JAVA_HOME значение C: \ Program Files \ Java \ jdk1.6.0_21.
Linux экспорт JAVA_HOME = / usr / local / java-current
макинтош export JAVA_HOME = / Библиотека / Java / Главная

Добавьте местоположение компилятора Java в системный путь.

Операционные системы Выход
Windows Добавьте строку C: \ Program Files \ Java \ jdk1.6.0_21 \ bin в конце системной переменной Path .
Linux экспорт PATH = $ PATH: $ JAVA_HOME / bin /
макинтош не требуется

Проверьте установку Java с помощью команды java -version, как описано выше.

Java Generics — Классы

Объявление универсального класса выглядит как объявление неуниверсального класса, за исключением того, что за именем класса следует раздел параметра типа.

Раздел параметров типа универсального класса может иметь один или несколько параметров типа, разделенных запятыми. Эти классы известны как параметризованные классы или параметризованные типы, потому что они принимают один или несколько параметров.

Синтаксис

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

куда

  • Box — Box — универсальный класс.

  • T — Параметр универсального типа, переданный в универсальный класс. Это может занять любой объект.

  • t — Экземпляр универсального типа T.

Box — Box — универсальный класс.

T — Параметр универсального типа, переданный в универсальный класс. Это может занять любой объект.

t — Экземпляр универсального типа T.

Описание

T — это параметр типа, передаваемый универсальному классу Box, и его следует передавать при создании объекта Box.

пример

Создайте следующую Java-программу, используя любой редактор по вашему выбору.

GenericsTester.java

Live Demo

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

Это даст следующий результат.

Выход

Integer Value :10
String Value :Hello World

Соглашения об именовании параметров типа

По соглашению имена параметров типа называются одинарными заглавными буквами, поэтому параметр типа можно легко отличить с помощью обычного имени класса или интерфейса. Ниже приведен список часто используемых имен параметров типа —

  • E — Элемент, и в основном используется фреймворком Java Collections.

  • K — ключ, и в основном используется для представления типа параметра ключа карты.

  • V — значение, и в основном используется для представления типа параметра значения карты.

  • N — число, и в основном используется для представления чисел.

  • T — Type, и в основном используется для представления первого параметра универсального типа.

  • S — Тип, и в основном используется для представления параметра второго универсального типа.

  • U — Тип, и в основном используется для представления третьего параметра универсального типа.

  • V — Тип, и в основном используется для представления четвертого параметра универсального типа.

E — Элемент, и в основном используется фреймворком Java Collections.

K — ключ, и в основном используется для представления типа параметра ключа карты.

V — значение, и в основном используется для представления типа параметра значения карты.

N — число, и в основном используется для представления чисел.

T — Type, и в основном используется для представления первого параметра универсального типа.

S — Тип, и в основном используется для представления параметра второго универсального типа.

U — Тип, и в основном используется для представления третьего параметра универсального типа.

V — Тип, и в основном используется для представления четвертого параметра универсального типа.

Следующий пример продемонстрирует вышеупомянутую концепцию.

пример

Создайте следующую Java-программу, используя любой редактор по вашему выбору.

GenericsTester.java

Live Demo

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

Это даст следующий результат.

Выход

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

Java Generics — вывод типа

Вывод типа представляет способность компилятора Java просматривать вызов метода и его соответствующее объявление для проверки и определения аргумента (ов) типа. Алгоритм вывода проверяет типы аргументов и, если доступно, возвращается назначенный тип. Алгоритмы вывода пытаются найти определенный тип, который может полностью заполнить все параметры типа.

Компилятор генерирует непроверенное предупреждение о преобразовании в случае, если вывод типа не используется.

Синтаксис

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

куда

  • Box — Box — универсальный класс.

  • <> — Оператор diamond обозначает вывод типа.

Box — Box — универсальный класс.

<> — Оператор diamond обозначает вывод типа.

Описание

Используя оператор diamond, компилятор определяет тип параметра. Этот оператор доступен начиная с версии Java SE 7 и далее.

пример

Создайте следующую Java-программу, используя любой редактор по вашему выбору.

GenericsTester.java

Live Demo

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

Это даст следующий результат.

Выход

Integer Value :10
String Value :Hello World

Java Generics — Методы

Вы можете написать одно объявление универсального метода, которое можно вызывать с аргументами разных типов. Основываясь на типах аргументов, передаваемых универсальному методу, компилятор обрабатывает каждый вызов метода соответствующим образом. Ниже приведены правила для определения общих методов —

  • Все объявления обобщенных методов имеют раздел параметров типа, ограниченный угловыми скобками (<и>), который предшествует типу возврата метода (<E> в следующем примере).

  • Каждый раздел параметров типа содержит один или несколько параметров типа, разделенных запятыми. Параметр типа, также известный как переменная типа, является идентификатором, который задает имя общего типа.

  • Параметры типа могут использоваться для объявления возвращаемого типа и использования в качестве заполнителей для типов аргументов, передаваемых универсальному методу, которые известны как фактические аргументы типа.

  • Тело универсального метода объявляется как тело любого другого метода. Обратите внимание, что параметры типа могут представлять только ссылочные типы, а не примитивные типы (такие как int, double и char).

Все объявления обобщенных методов имеют раздел параметров типа, ограниченный угловыми скобками (<и>), который предшествует типу возврата метода (<E> в следующем примере).

Каждый раздел параметров типа содержит один или несколько параметров типа, разделенных запятыми. Параметр типа, также известный как переменная типа, является идентификатором, который задает имя общего типа.

Параметры типа могут использоваться для объявления возвращаемого типа и использования в качестве заполнителей для типов аргументов, передаваемых универсальному методу, которые известны как фактические аргументы типа.

Тело универсального метода объявляется как тело любого другого метода. Обратите внимание, что параметры типа могут представлять только ссылочные типы, а не примитивные типы (такие как int, double и char).

пример

Следующий пример иллюстрирует, как мы можем напечатать массив другого типа, используя один общий метод —

Live Demo

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

Это даст следующий результат —

Выход

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

Java Generics — Параметры нескольких типов

Универсальный класс может иметь параметры нескольких типов. Следующий пример продемонстрирует вышеупомянутую концепцию.

пример

Создайте следующую Java-программу, используя любой редактор по вашему выбору.

GenericsTester.java

Live Demo

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

Это даст следующий результат.

Выход

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

Java Generics — Параметризованные Типы

Универсальный класс может иметь параметризованные типы, где параметр типа может быть заменен параметризованным типом. Следующий пример продемонстрирует вышеупомянутую концепцию.

пример

Создайте следующую Java-программу, используя любой редактор по вашему выбору.

GenericsTester.java

Live Demo

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

Это даст следующий результат.

Выход

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

Обобщения Java — необработанные типы

Необработанный тип — это объект универсального класса или интерфейса, если его аргументы типа не передаются при его создании. Следующий пример продемонстрирует вышеупомянутую концепцию.

пример

Создайте следующую Java-программу, используя любой редактор по вашему выбору.

GenericsTester.java

Live Demo

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

Это даст следующий результат.

Выход

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

Java Generics — Параметры Ограниченного Типа

Могут возникнуть ситуации, когда вы захотите ограничить типы типов, которые разрешено передавать в параметр типа. Например, метод, который работает с числами, может хотеть принимать только экземпляры Number или его подклассов. Это то, для чего предназначены параметры ограниченного типа.

Чтобы объявить параметр ограниченного типа, укажите имя параметра типа, затем ключевое слово extends и его верхнюю границу.

пример

Следующий пример иллюстрирует, как extends используется в общем смысле для обозначения «extends» (как в классах) или «Implements» (как в интерфейсах). Этот пример — универсальный метод для возврата самого большого из трех сопоставимых объектов —

Live Demo

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

Это даст следующий результат —

Выход

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

Java Generics — несколько границ

Параметр типа может иметь несколько границ.

Синтаксис

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

куда

  • максимум — максимум это общий метод.

  • T — Параметр универсального типа, переданный в универсальный метод. Это может занять любой объект.

максимум — максимум это общий метод.

T — Параметр универсального типа, переданный в универсальный метод. Это может занять любой объект.

Описание

T — это параметр типа, передаваемый универсальному классу Box, и он должен быть подтипом класса Number и должен влиять на сопоставимый интерфейс. Если класс передается как связанный, он должен быть передан прежде, чем интерфейс, иначе произойдет ошибка времени компиляции.

пример

Создайте следующую Java-программу, используя любой редактор по вашему выбору.

Live Demo

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;   
   }*/
}

Это даст следующий результат —

Выход

Max of 3, 4 and 5 is 5

Max of 6.6,8.8 and 7.7 is 8.8

Java Generics — Список

Java предоставила общую поддержку в интерфейсе List.

Синтаксис

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

куда

  • list — объект интерфейса List.

  • T — Параметр универсального типа, переданный во время объявления списка.

list — объект интерфейса List.

T — Параметр универсального типа, переданный во время объявления списка.

Описание

T — это параметр типа, передаваемый универсальному интерфейсу List и его классу реализации ArrayList.

пример

Создайте следующую Java-программу, используя любой редактор по вашему выбору.

Live Demo

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

Это даст следующий результат —

Выход

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

Java Generics — Set

Java предоставила общую поддержку в интерфейсе Set.

Синтаксис

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

куда

  • set — объект Set Interface.

  • T — Параметр универсального типа, переданный во время объявления набора.

set — объект Set Interface.

T — Параметр универсального типа, переданный во время объявления набора.

Описание

T — это параметр типа, передаваемый универсальному интерфейсу Set и его классу реализации HashSet.

пример

Создайте следующую Java-программу, используя любой редактор по вашему выбору.

Live Demo

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

Это даст следующий результат —

Выход

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

Java Generics — Карта

Java предоставила общую поддержку в интерфейсе карты.

Синтаксис

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

куда

  • set — объект Set Interface.

  • T — Параметр универсального типа, переданный во время объявления набора.

set — объект Set Interface.

T — Параметр универсального типа, переданный во время объявления набора.

Описание

T — это параметр типа, передаваемый универсальному интерфейсу Set и его классу реализации HashSet.

пример

Создайте следующую Java-программу, используя любой редактор по вашему выбору.

Live Demo

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

Это даст следующий результат —

Выход

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

Обобщения Java — подстановочные знаки с ограничением сверху

Вопросительный знак (?), Представляет подстановочный знак, обозначает неизвестный тип в обобщениях. Могут возникнуть ситуации, когда вы захотите ограничить типы типов, которые разрешено передавать в параметр типа. Например, метод, который работает с числами, может хотеть принимать только экземпляры Number или его подклассов.

Чтобы объявить ограниченный верхним параметром Wildcard, перечислите?, Затем ключевое слово extends и его верхнюю границу.

пример

В следующем примере показано, как extends используется для указания подстановочного знака верхней границы.

Live Demo

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

Это даст следующий результат —

Выход

sum = 6.0
sum = 7.0

Обобщения Java — неограниченные подстановочные знаки

Вопросительный знак (?), Представляет подстановочный знак, обозначает неизвестный тип в обобщениях. Могут быть случаи, когда любой объект может использоваться, когда метод может быть реализован с использованием функциональных возможностей, предоставляемых в классе Object, или когда код не зависит от параметра типа.

Чтобы объявить параметр Unbounded Wildcard, перечислите? только.

пример

В следующем примере показано, как extends используется для указания неограниченного подстановочного знака.

Live Demo

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

Это даст следующий результат —

Выход

1 
2 
3 
1.2 
2.3 
3.5 

Java Generics — Подстановочные знаки с ограничением снизу

Вопросительный знак (?), Представляет подстановочный знак, обозначает неизвестный тип в обобщениях. Могут возникнуть ситуации, когда вы захотите ограничить типы типов, которые разрешено передавать в параметр типа. Например, метод, который работает с числами, может хотеть принимать только экземпляры Integer или его суперклассы, такие как Number.

Чтобы объявить ограниченный снизу параметр Wildcard, перечислите?, Затем ключевое слово super и нижнюю границу.

пример

В следующем примере показано, как super используется для указания подстановочного знака нижней границы.

Live Demo

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

Это даст следующий результат —

Cat Added
Cat Added

Java Generics — Руководство по использованию подстановочных знаков

Подстановочные знаки можно использовать тремя способами:

  • Верхний предел Wildcard -? Тип расширяет.

  • Нижний предел подстановочный знак -? Супер Тип.

  • Неограниченный Wildcard -?

Верхний предел Wildcard -? Тип расширяет.

Нижний предел подстановочный знак -? Супер Тип.

Неограниченный Wildcard -?

Чтобы решить, какой тип подстановочного знака лучше всего соответствует условию, давайте сначала классифицируем тип параметров, передаваемых методу, как параметр in и out .

  • в переменной — переменная в предоставляет данные для кода. Например, копия (src, dest). Здесь src действует как переменная, являющаяся данными для копирования.

  • Переменная outпеременная out содержит данные, обновленные кодом. Например, копия (src, dest). Здесь dest действует как в переменной скопировав данные.

в переменной — переменная в предоставляет данные для кода. Например, копия (src, dest). Здесь src действует как переменная, являющаяся данными для копирования.

Переменная outпеременная out содержит данные, обновленные кодом. Например, копия (src, dest). Здесь dest действует как в переменной скопировав данные.

Руководство по подстановочным знакам.

  • Подстановочный знак с верхней границей — если переменная относится к категории, используйте ключевое слово extends с подстановочным знаком.

  • Подстановочный знак нижней границы — если переменная не относится к категории, используйте ключевое слово super с подстановочным знаком.

  • Неограниченный подстановочный знак — если к переменной можно получить доступ, используя метод класса Object, тогда используйте несвязанный подстановочный знак.

  • Без подстановочных знаков — если код обращается к переменной как в категории «как», так и вне ее, не используйте подстановочные знаки.

Подстановочный знак с верхней границей — если переменная относится к категории, используйте ключевое слово extends с подстановочным знаком.

Подстановочный знак нижней границы — если переменная не относится к категории, используйте ключевое слово super с подстановочным знаком.

Неограниченный подстановочный знак — если к переменной можно получить доступ, используя метод класса Object, тогда используйте несвязанный подстановочный знак.

Без подстановочных знаков — если код обращается к переменной как в категории «как», так и вне ее, не используйте подстановочные знаки.

пример

Следующий пример иллюстрирует вышеупомянутые понятия.

Live Demo

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

Это даст следующий результат —

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

Java Generics — стирание типов

Обобщения используются для более строгих проверок типов во время компиляции и для обеспечения общего программирования. Чтобы реализовать общее поведение, Java-компилятор применяет стирание типа. Стирание типа — это процесс, в котором компилятор заменяет универсальный параметр фактическим методом класса или моста. При стирании типа компилятор гарантирует, что никакие дополнительные классы не создаются и нет никаких накладных расходов времени выполнения.

Тип Правила стирания

  • Замените параметры типа в универсальном типе их границей, если используются параметры ограниченного типа.

  • Замените параметры типа в универсальном типе на Object, если используются параметры неограниченного типа.

  • Вставьте слепки типа, чтобы сохранить безопасность типа.

  • Генерация мостовых методов для сохранения полиморфизма в расширенных универсальных типах.

Замените параметры типа в универсальном типе их границей, если используются параметры ограниченного типа.

Замените параметры типа в универсальном типе на Object, если используются параметры неограниченного типа.

Вставьте слепки типа, чтобы сохранить безопасность типа.

Генерация мостовых методов для сохранения полиморфизма в расширенных универсальных типах.

Java Generics — стирание связанных типов

Компилятор Java заменяет параметры типа в универсальном типе их границей, если используются параметры ограниченного типа.

пример

Live Demo

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

В этом случае java-компилятор заменит T на Number-класс, и после стирания типа, компилятор сгенерирует байт-код для следующего кода.

Live Demo

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

В обоих случаях результат один и тот же —

Выход

Integer Value :10
Double Value :10.0

Java Generics — стирание неограниченных типов

Компилятор Java заменяет параметры типа в универсальном типе на Object, если используются параметры неограниченного типа.

пример

Live Demo

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

В этом случае java-компилятор заменит T на класс Object, и после стирания типа, компилятор сгенерирует байт-код для следующего кода.

Live Demo

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

В обоих случаях результат один и тот же —

Выход

Integer Value :10
String Value :Hello World

Java Generics — Стирание универсальных методов

Компилятор Java заменяет параметры типа в универсальном типе на Object, если используются параметры неограниченного типа, и на type, если связанные параметры используются в качестве параметров метода.

пример

Live Demo

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

В этом случае java-компилятор заменит T на класс Object, и после стирания типа, компилятор сгенерирует байт-код для следующего кода.

Live Demo

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

В обоих случаях результат один и тот же —

Выход

Integer Value :10
String Value :Hello World

Java Generics — нет примитивных типов

При использовании обобщенных типов примитивные типы нельзя передавать в качестве параметров типа. В приведенном ниже примере, если мы передадим тип int примитива в класс box, компилятор будет жаловаться. Чтобы смягчить то же самое, нам нужно передать объект Integer вместо примитивного типа int.

пример

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

Это даст следующий результат —

Выход

Value: 10

Java Generics — без экземпляра

Параметр типа нельзя использовать для создания экземпляра своего объекта внутри метода.

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

пример

Live Demo

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

Это даст следующий результат —

Item added.

Java Generics — нет статического поля

При использовании шаблонов параметры типа не могут быть статическими. Поскольку статическая переменная является общей для объекта, компилятор не может определить, какой тип использовать. Рассмотрим следующий пример, если параметры статического типа были разрешены.

пример

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

Поскольку stringBox и integerBox имеют переменную статического типа, ее тип не может быть определен. Следовательно, параметры статического типа не допускаются.

Java Generics — без ролей

Преобразование в параметризованный тип не допускается, если оно не параметризовано неограниченными подстановочными знаками.

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;

Чтобы достичь того же, можно использовать неограниченные символы подстановки.

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

Java Generics — Нет instanceOf

Поскольку компилятор использует стирание типа, среда выполнения не отслеживает параметры типа, поэтому во время выполнения разницу между Box <Integer> и Box <String> невозможно проверить с помощью оператора 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>) { }

Java Generics — без массива

Массивы параметризованных типов недопустимы.

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

Поскольку компилятор использует стирание типа, параметр типа заменяется на Объект, и пользователь может добавить любой объект в массив. И во время выполнения код не сможет генерировать 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>();  

Java Generics — без исключений

Универсальный класс не может расширять класс 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 {}

Метод не может перехватить экземпляр параметра типа.

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) { 
         // ...
   }
} 

Параметры типа разрешены в предложении throws.

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

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

   public int get() {
      return t;
   }   
}

Java Generics — без перегрузки

Класс не может иметь два перегруженных метода, которые могут иметь одну и ту же сигнатуру после стирания типа.