Обобщения Java

Java 泛型是 JDK 5 引入的新特性。泛型提供了一种编译时类型安全检测机制,允许程序员在编译时检测到非法类型。

泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数。

假设我们有这样一个需求:写一个排序方法,能够对整型数组、字符串数组甚至其他任何类型的数组进行排序,该如何实现?

答案是使用Java 泛型。

使用 Java 泛型的概念,我们可以写一个泛型方法来对一个对象数组排序。然后,调用该泛型方法来对整型数组、浮点数数组、字符串数组等进行排序。


Обобщенные методы

你可以写一个泛型方法,该方法在调用时可以接收不同类型的参数。根据传递给泛型方法的参数类型,编译器适当地处理每一个方法调用。

以下是定义泛型方法的规则:

  • 所有泛型方法声明都有一个类型参数声明部分(由尖括号分隔)。类型参数声明部分出现在方法返回类型之前(如下例所示,<E>)。
  • Каждый раздел объявления параметров типа содержит один или несколько параметров типа, разделённых запятыми. Обобщённый параметр, также известный как переменная типа, — это идентификатор, используемый для указания имени обобщённого типа.
  • Параметры типа можно использовать для объявления возвращаемых типов, и они могут служить заполнителями для фактических типов параметров, получаемых обобщённым методом.
  • Объявление тела обобщённого метода такое же, как и для других методов. Обратите внимание, что параметры типа могут представлять только ссылочные типы, а не примитивные типы (такие какint、double、charи т. д.).

Обобщённые маркеры в Java:

  • E- Элемент (используется в коллекциях, так как коллекции хранят элементы)
  • T- Тип (класс Java)
  • K- Ключ
  • V- Значение
  • N- Число (числовой тип)
  • ?- обозначает неопределённый тип Java

Пример

В следующем примере показано, как использовать обобщённый метод для вывода элементов массива различных типов:

Пример

public class GenericMethodTest { //Обобщённый метод printArray public static < E > void printArray( E[] inputArray ) { //Вывод элементов массива for ( E element : inputArray ){ System.out.printf( "%s ", element ); } System.out.println(); } public static void main( String args[] ) { //Создание массивов разных типов: Integer, Double и 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( "Элементы массива Integer:" ); printArray( intArray ); //Передача целочисленного массива System.out.println( "\nЭлементы массива с двойной точностью:" ); printArray( doubleArray ); //Передача массива с двойной точностью System.out.println( "\nЭлементы символьного массива:" ); printArray( charArray ); //Передача символьного массива } }

Скомпилируйте приведённый выше код, и результат выполнения будет следующим:

整型数组元素为:
1 2 3 4 5 

双精度型数组元素为:
1.1 2.2 3.3 4.4 

字符型数组元素为:
H E L L O 

Ограниченные параметры типа:

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

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

Пример

В следующем примере показано, как "extends" используется в общем смысле "extends" (классы) или "implements" (интерфейсы). Обобщённый метод в этом примере возвращает максимум из трёх сравнимых объектов.

Пример

public class MaximumTest { //Сравнить три значения и вернуть максимум public static <T extends Comparable<T>> T maximum(T x, T y, T z) { T max = x; //Предположим, x — начальный максимум if ( y.compareTo( max ) > 0 ){ max = y; //y больше } if ( z.compareTo( max ) > 0 ){ max = z; //Теперь z больше } return max; //Возвращает максимальный объект } public static void main( String args[] ) { System.out.printf( "Наибольшее из %d, %d и %d равно %d\n\n", 3, 4, 5, maximum( 3, 4, 5 ) ); System.out.printf( "Наибольшее из %.1f, %.1f и %.1f равно %.1f\n\n", 6.6, 8.8, 7.7, maximum( 6.6, 8.8, 7.7 ) ); System.out.printf( "Наибольшее из %s, %s и %s равно %s\n","pear", "apple", "orange", maximum( "pear", "apple", "orange" ) ); } }

Скомпилируйте приведённый выше код, и результат выполнения будет следующим:

3, 4 和 5 中最大的数为 5

6.6, 8.8 和 7.7 中最大的数为 8.8

pear, apple 和 orange 中最大的数为 pear

Обобщённые классы

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

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

Пример

В следующем примере показано, как мы определяем обобщённый класс:

Пример

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("Example")); System.out.printf("Integer value: %d\n\n", integerBox.get()); System.out.printf("String: %s\n", stringBox.get()); } }

Скомпилируйте приведённый выше код, и результат выполнения будет следующим:

整型值为 :10

字符串为 :Example

Подстановочные знаки типов

1. Подстановочные знаки типов обычно используют?для замены конкретных параметров типа. Например,List<?>логически являетсяList<String>,List<Integer>и всеList<конкретные аргументы типа>родительские классы.

Пример

import java.util.*; public class GenericTest { public static void main(String[] args) { List<String> name = new ArrayList<String>(); List<Integer> age = new ArrayList<Integer>(); List<Number> number = new ArrayList<Number>(); name.add("icon"); age.add(18); number.add(314); getData(name); getData(age); getData(number); } public static void getData(List<?> data) { System.out.println("data :" + data.get(0)); } }

Результат вывода:

data :icon
data :18
data :314

Анализ:ПосколькуgetData()параметр метода имеет типList<?>, поэтомуname,age,numberвсе могут быть использованы в качестве аргументов этого метода; в этом и заключается функция подстановочных знаков.

2. Верхняя граница подстановочного знака типа определяется с помощью формы List

Пример

import java.util.*; public class GenericTest { public static void main(String[] args) { List<String> name = new ArrayList<String>(); List<Integer> age = new ArrayList<Integer>(); List<Number> number = new ArrayList<Number>(); name.add("icon"); age.add(18); number.add(314); //getUperNumber(name);//1 getUperNumber(age);//2 getUperNumber(number);//3 } public static void getData(List<?> data) { System.out.println("data :" + data.get(0)); } public static void getUperNumber(List<? extends Number> data) { System.out.println("data :" + data.get(0)); } }

Результат вывода:

data :18
data :314

Анализ:В//1Возникнет ошибка в позиции, потому чтоgetUperNumber()параметр в методе уже ограничил верхнюю границу обобщенного типа параметра доNumber, поэтому обобщенный тип, являющийсяString, не входит в этот диапазон, поэтому возникает ошибка.

3. Нижняя граница подстановочного знака типа определяется через формуList<? super Number>для его определения, означая, что тип может принимать толькоNumberи его супертипы выше него, такие какObjectэкземпляры этого типа.

Другие расширения