Generic (дженерики — обобщенные типы)
Параметризация типов
- Класс или интерфейс может быть объявлен обобщенным (generic):
- class List< T >
- class Map< K, V >
- class NumberList< T extends Number >
- class X< T extends Y & Z >
- Здесь T, K, V - параметры типа, а сам класс задает параметризованный тип.
- Параметр типа может использоваться внутри нестатических членов класса:
- Тип поля, параметра, переменной
- Возвращаемый тип метода
- Подставляться параметром другого типа
- Для использования параметризованного типа необходимо предоставить все параметры (с помощью явных не примитивных типов, других параметров типов либо маски)
Пример параметризованного класса
import java.util.NoSuchElementException;
static class Option<T> {
T value;
public Option(T value) {
this.value = value;
}
public T get() {
if (value == null) throw new NoSuchElementException();
return value;
}
public void set(T newValue) {
value = newValue;
}
public T orElse(T other) {
return value == null ? other : value;
}
public boolean isPresent() {
return value != null;
}
}
Работа с параметризованным классом
public class Main {
public static void main(String[] args) {
Option<String> present = new Option<>("yes");
Option<String> absent = new Option<>(null);
System.out.println(present.isPresent());
System.out.println(present.get());
System.out.println(absent.isPresent());
System.out.println(absent.orElse("no"));
}
}
Маскировочный символ (wildcard)
(“?” = “? extends Object”)
public class Main {
public static void main(String[] args) {
/* "?" вариативность в точке использования */
Option<?> present = new Option<>("yes");
System.out.println(present.isPresent());
Object value = present.get();
System.out.println(value);
/* set only null */
present.set(null);
/* not work */
present.set(123);
}
}
public class Main {
public static void main(String[] args) {
Option<? extends Number> number = new Option<>(123);
Number n = number.get();
/* set only null */
present.set(null);
/* not work */
present.set(987);
}
}
Наследуем
class NumberOption<N extends Number> extends Option<N> {
public NumberOption(N value) {
super(value);
}
}
class IntegerOption extends Option<Integer> {
public IntegerOption(Integer value) {
super(value);
}
}
class Main {
public static void main(String[] args) {
NumberOoption<?> number = new NumberOption<>(123);
Number n = number.get();
/* not work */
number.set(987);
IntegerOption integer = new IntegerOption(123);
Integer i = integer.get();
integer.set(987);
number = integer;
}
}
Параметризованные методы и конструкторы
class OptionUtils {
static <T> void setNotNull(Option<? super T> option, T value) {
if (value == null) throw new IllegalArgumentException();
option.set(value);
}
}
class Main {
public static void main(String[] args) {
OptionUtils.<Number>setNotNull(n, 123);
}
}
!!! Не использовать массивы параметризованных объектов
Varargs + generics:
Полезно, но осторожно
class Main {
/* Проверка. Встречается ли value в списке options */
@SafeVarargs
static <T> boolean isOneOf(T value, T... options) {
for (T option : options) {
if (Objects.equals(value, option)) return true;
}
return false;
}
public static void main(String[] args) {
isOneOf(option, new Option("foo"), new Option("bar"));
}
}