Дженерики в Java
С помощью дженериков мы можем писать классы и методы, которые не привязаны только к одному типу. Тип данных указываются в качестве параметров, которые должны быть ссылочным типом.
Основными преимуществами дженериков являются повторное использование кода (очевидно), а также безопасность типов:
До появления дженериков (JDK 5) можно было использовать корневой класс Object в качестве члена класса, не ограничивая его одним типом. Но это было не безопасно.
Другие могут изменить ваш объект и превратить его во что угодно и вызвать ошибку, которая будет обнаружена только во время выполнения (помните, что в этом случае компилятор ничего не знает о типе данных).
С дженериками все приведения типов выполняются автоматически и безопасность.
Как это реализовано?
Компилятор Java не создает разные версии дженериков для каждого типа T.
Вместо этого он заменяет дженерики их связанными типами. Например, class Data<T extends Number>{ T num } заменяется на class Data{ Number num }.
Обратите внимание, что этот процесс также обеспечивает обратную совместимость со старыми версиями Java.
С другой стороны, нельзя написать что-то вроде этого:
ArrayList<Integer> list = new ArrayList<>();
// won't compile
System.out.println(list instanceof ArrayList<Integer>);
// but this is true
System.out.println(list instanceof ArrayList);
В то время как в C# дженерики реализованы по-другому:
Console.WriteLine(new List<string>() is List<string>);
// that's fine in C#
простой пример безопасных типов (Java):
class Something{ Object obj; }
class Something{
Object obj;
}
class SomethingGeneric<T extends Number>{
T t;
}
// somewhere else in the code:
Something s = new Something();
s.obj = 1;
String str1 = (String) s.obj; // ClassCastException
SomethingGeneric<Integer> gen = new SomethingGeneric();
gen.t = 2;
String str2 = (String) gen.t; // won't compile
gen.t = "2"; // won't compile
Следующий пример иллюстрирует, как мы можем напечатать массив другого типа, используя один общий метод .
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
}
}
Следующий пример иллюстрирует применение метода для возврата наибольшего из трех объектов Comparable.
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