Введение в обобщения
Часть раздела Объектно-ориентированное программирование путешествия по Java на Coddy. Урок 53 из 87.
Представь, что тебе нужно создать класс, который хранит значение и возвращает его позже. Без обобщений пришлось бы писать отдельный класс для каждого типа: один для Integer, один для String, один для Double и так далее. Это приводит к дублированию кода и проблемам с его поддержкой.
Обобщения решают эту проблему, позволяя писать классы и методы, которые работают с типами любого вида, сохраняя при этом безопасность типов. Вы определяете заполнитель (называемый параметром типа), который заменяется фактическим типом при использовании класса:
// Без обобщений — используется Object, требуется приведение типа
List list = new ArrayList();
list.add("Hello");
String s = (String) list.get(0); // Требуется явное приведение типа
// С обобщениями — обеспечивается типобезопасность, приведение типа не требуется
List<String> list = new ArrayList<String>();
list.add("Hello");
String s = list.get(0); // Приведение типа не требуетсяУгловые скобки <T> обозначают параметр типа. Когда ты пишешь List<String>, ты сообщаешь компилятору, что этот список будет содержать только строки. Если ты попытаешься добавить целое число, компилятор сразу обнаружит ошибку, а не позволит ей проявиться во время выполнения.
Обобщения дают два ключевых преимущества: они устраняют необходимость в явном приведении типов и позволяют обнаруживать ошибки типов во время компиляции, а не во время выполнения. Благодаря этому код становится безопаснее и чище. В следующих уроках мы рассмотрим, как создавать собственные обобщённые классы и методы.
Задание
ЛегкоДавайте создадим простую систему хранения, которая покажет преимущества обобщений: сравним старый подход (использование Object с приведением типов) и новый подход (использование обобщений с проверкой типов).
Чтобы увидеть разницу на практике, вы создадите три файла:
OldBox.java: создайте класс, который хранит значения по старому, дообобщённому принципу. В вашем OldBox должно быть приватное полеcontentтипаObject. Добавьте конструктор, который принимает Object и сохраняет его, а также методgetContent(), который возвращает Object. Этот подход работает, но при получении значений требуется приведение типов.GenericBox.java: теперь создайте современную версию с проверкой типов, используя обобщения. В объявлении вашего GenericBox должен использоваться параметр типаT. В нём должны быть приватное полеcontentтипаT, конструктор, принимающий значение типаT, и методgetContent(), возвращающий типT. При использовании этого класса приведение типов не требуется!Main.java: продемонстрируйте оба подхода рядом. Вы получите два входных значения: слово (String) и число (целое число).Сначала используйте старый подход: создайте OldBox, хранящий слово, затем получите его, приведя к String, и выведите:
OldBox (with cast): [value]Затем используйте подход с обобщениями: создайте
GenericBox<String>, хранящий то же слово, получите его без приведения типов и выведите:GenericBox (no cast): [value]Затем продемонстрируйте работу обобщений с целыми числами: создайте
GenericBox<Integer>, хранящий число, получите его и выведите:GenericBox Integer: [value]Наконец, выведите пустую строку, а затем:
Type safety: Generics catch errors at compile time!
Вы получите два входных значения по порядку: слово (String) и число (целое число в виде String, которое нужно разобрать).
Обратите внимание на ключевое отличие: с OldBox нужно привести тип с помощью (String) box.getContent() и надеяться, что тип указан правильно. С GenericBox компилятор знает тип, и приведение типов не требуется. В этом и заключается основное преимущество обобщений: проверка типов без хлопот с приведением типов!
Попробуйте сами
import java.util.Scanner;
class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
String word = scanner.nextLine();
int number = Integer.parseInt(scanner.nextLine());
// TODO: Создайте OldBox, в котором хранится слово
// Получите значение с приведением к String и выведите: "OldBox (with cast): [value]"
// TODO: Создайте GenericBox<String>, в котором хранится слово
// Получите значение без приведения типа и выведите: "GenericBox (no cast): [value]"
// TODO: Создайте GenericBox<Integer>, в котором хранится число
// Получите значение и выведите: "GenericBox Integer: [value]"
// TODO: Выведите пустую строку, затем выведите:
// "Type safety: Generics catch errors at compile time!"
}
}
В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.
Все уроки раздела Объектно-ориентированное программирование
1Основы ООП
Внешние файлыВведение в ООПКлассы и объектыКлючевое слово thisМетодыПоля (атрибуты)Метод-конструкторПерегрузка конструкторовИтоги — Простой калькулятор4Наследование
Основы наследования (extends)Ключевое слово superПереопределение методов (@Override)Цепочка конструкторовКласс ObjectОдиночное и многоуровневое наследованиеПочему нет множественного наследования классовИтоги — Иерархия сотрудников7Специальные методы и класс Object
Метод toString()equals() и hashCode()Метод clone()compareTo() и ComparableИнтерфейс ComparatorПовторение — Кастомная сортировка2Модификаторы доступа и инкапсуляция
Обзор уровней доступаМетоды геттеры и сеттерыСокрытие данныхКлючевое слово finalИтоги — Менеджер банковского счета5Полиморфизм
Основы перегрузки методовПереопределение методов (Run-Time)Upcasting и DowncastingОператор instanceofАбстрактные классы и методыИтоги: Калькулятор фигур8Продвинутые концепции ООП
Композиция против наследованияАгрегация против композицииВнутренние, вложенные и анонимные классыEnums и методы перечисленийRecords (Java 16+)Sealed-классы (Java 17+)11Паттерны проектирования. Часть 1
Введение в паттерны проектированияПаттерн SingletonПаттерн FactoryПаттерн BuilderПаттерн ObserverПаттерн Strategy3Свойства класса и статические члены
Переменные экземпляра и статические переменныеСтатические методыСтатические блокиКонстанты (static final)Итоги: Счетчик и утилиты6Интерфейсы и абстрактные классы
Введение в интерфейсыРеализация интерфейсовРеализация нескольких интерфейсовDefault и Static методы в интерфейсахАбстрактные классы vs ИнтерфейсыФункциональные интерфейсыИтоги — Платежная система9Обобщения
Введение в обобщенияОбобщенные классыОбобщенные методыОграниченные параметры типаСимволы подстановки (Wildcards: ?, extends, super)Итоги — Обобщенный контейнер12Паттерны проектирования. Часть 2
Паттерн «Команда»Паттерн «Адаптер»Паттерн «Декоратор»Паттерн «Шаблонный метод»Паттерн «Состояние»Паттерн «Компоновщик»Паттерн «Итератор»Потренируйтесь самостоятельно: Онлайн-компилятор Java