Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
# Дженерики (Generics) в Java
Дженерики — это механизм, позволяющий создавать классы, интерфейсы и методы, которые работают с типизированными параметрами. Благодаря дженерикам код становится более безопасным, переиспользуемым и читаемым.
Основная идея
Дженерики позволяют определить тип данных при создании или использовании класса:
// Без дженериков (старый способ)
List list = new ArrayList();
list.add("Hello");
String str = (String) list.get(0); // нужна явная типизация
// С дженериками (современный способ)
List<String> list = new ArrayList<>();
list.add("Hello");
String str = list.get(0); // типизация уже известна
1. Дженерик класс
// T — это параметр типа (type parameter)
public class Container<T> {
private T value;
public void setValue(T value) {
this.value = value;
}
public T getValue() {
return value;
}
}
// Использование
Container<String> stringContainer = new Container<>();
stringContainer.setValue("Hello");
String result = stringContainer.getValue(); // String, без типизации
Container<Integer> intContainer = new Container<>();
intContainer.setValue(42);
Integer intResult = intContainer.getValue(); // Integer
2. Дженерик метод
public class Util {
// Дженерик метод
public static <T> void printArray(T[] array) {
for (T element : array) {
System.out.println(element);
}
}
// Дженерик метод с ограничением типа
public static <T extends Number> double sum(T[] numbers) {
double total = 0;
for (T number : numbers) {
total += number.doubleValue();
}
return total;
}
}
// Использование
String[] strings = {"A", "B", "C"};
Util.printArray(strings);
Integer[] integers = {1, 2, 3};
Util.printArray(integers);
System.out.println(Util.sum(integers)); // 6.0
3. Ограничения типов (Bounds)
Верхняя граница (extends)
// T может быть Number или его подклассом
public class Calculator<T extends Number> {
public double getDoubleValue(T value) {
return value.doubleValue();
}
}
Calculator<Integer> calc1 = new Calculator<>();
Calculator<Double> calc2 = new Calculator<>();
// Calculator<String> calc3 = new Calculator<>(); // Error! String не extends Number
Множественные границы
// T должен быть Comparable И Serializable
public class Comparator<T extends Comparable<T> & Serializable> {
public boolean isGreater(T a, T b) {
return a.compareTo(b) > 0;
}
}
4. Wildcards (подстановочные типы)
? (любой тип)
public void printList(List<?> list) {
for (Object obj : list) {
System.out.println(obj);
}
}
List<String> strings = Arrays.asList("A", "B");
List<Integer> integers = Arrays.asList(1, 2);
printList(strings);
printList(integers);
? extends T (верхняя граница)
// Читаем данные, но не пишем
public void readNumbers(List<? extends Number> numbers) {
for (Number n : numbers) {
System.out.println(n);
}
}
List<Integer> integers = Arrays.asList(1, 2, 3);
List<Double> doubles = Arrays.asList(1.0, 2.0);
readNumbers(integers);
readNumbers(doubles);
? super T (нижняя граница)
// Пишем данные, но не гарантируем тип при чтении
public void writeNumbers(List<? super Number> numbers) {
numbers.add(10);
numbers.add(20.5);
}
List<Number> numList = new ArrayList<>();
List<Object> objList = new ArrayList<>();
writeNumbers(numList);
writeNumbers(objList);
5. Практические примеры
Дженерик стек
public class Stack<T> {
private List<T> elements = new ArrayList<>();
public void push(T element) {
elements.add(element);
}
public T pop() {
if (isEmpty()) {
throw new EmptyStackException();
}
return elements.remove(elements.size() - 1);
}
public boolean isEmpty() {
return elements.isEmpty();
}
}
// Использование
Stack<String> stack = new Stack<>();
stack.push("First");
stack.push("Second");
System.out.println(stack.pop()); // Second
Дженерик пара
public class Pair<K, V> {
private K key;
private V value;
public Pair(K key, V value) {
this.key = key;
this.value = value;
}
public K getKey() { return key; }
public V getValue() { return value; }
}
// Использование
Pair<String, Integer> person = new Pair<>("Alice", 30);
System.out.println(person.getKey()); // Alice
System.out.println(person.getValue()); // 30
Дженерик DAO
public interface Repository<T, ID> {
void save(T entity);
T findById(ID id);
List<T> findAll();
void delete(T entity);
}
public class UserRepository implements Repository<User, Long> {
@Override
public void save(User user) {
// сохранение пользователя
}
@Override
public User findById(Long id) {
// поиск по ID
return null;
}
@Override
public List<User> findAll() {
return new ArrayList<>();
}
@Override
public void delete(User user) {
// удаление
}
}
6. Type Erasure (стирание типов)
В runtime Java стирает информацию о дженериках:
List<String> strings = new ArrayList<>();
List<Integer> integers = new ArrayList<>();
// В runtime:
// strings.getClass() == integers.getClass() == ArrayList.class
System.out.println(strings.getClass() == integers.getClass()); // true!
Поэтому нельзя создавать массивы дженериков:
// ❌ Error!
// List<String>[] array = new List<String>[10];
// ✅ Правильно:
List<String>[] array = new List[10]; // raw type
Преимущества дженериков
- Типобезопасность — ошибки типов выявляются в compile-time
- Избежание типизации — не нужны явные cast
- Переиспользование — один код работает с разными типами
- Лучшая документация — сразу видно, с какими типами работает класс
Практические рекомендации
- Используй дженерики везде, где работаешь с коллекциями
- Используй wildcards для большей гибкости
- Не используй raw types без необходимости
- Помни о type erasure при работе с reflection
Дженерики — один из самых мощных механизмов Java для написания гибкого и безопасного кода.