Введение в обобщения
Часть раздела Объектно-ориентированное программирование путешествия по Java на Coddy. Урок 53 из 87.
Представьте, что вам нужно создать класс, который хранит значение и извлекает его позже. Без generics вам пришлось бы писать отдельные классы для каждого типа: один для Integer, один для String, один для Double и так далее. Это приводит к дублированию кода и сложностям с сопровождением.
Generics решают эту проблему, позволяя писать классы и методы, работающие с любым типом, при этом сохраняя безопасность типов. Вы определяете заполнитель (называемый параметром типа), который заменяется фактическим типом при использовании класса:
// Без дженериков — использует 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>, вы сообщаете компилятору, что этот список будет содержать только строки. Если вы попытаетесь добавить целое число, компилятор немедленно обнаружит ошибку, вместо того чтобы ошибка возникла во время выполнения.
Generics дают два ключевых преимущества: они устраняют необходимость в явном casting и обнаруживают ошибки типов на этапе compile, а не во время выполнения. Благодаря этому ваш код становится одновременно безопаснее и чище. В следующих уроках мы рассмотрим, как создавать собственные обобщённые class и method.
Задание
ЛегкоДавайте создадим простую систему хранения, которая продемонстрирует возможности обобщений, сравнив старый способ (с использованием Object и приведения типов) с новым способом (с использованием обобщений и проверкой типов).
Вы создадите три файла, чтобы увидеть разницу на практике:
OldBox.java: Create класс, который хранит значения способом, использовавшимся до обобщений. Ваш OldBox должен иметь private полеcontentтипаObject. Добавьте конструктор, который accepts Object и сохраняет его, а также методgetContent(), который возвращает Object. Этот подход работает, но requires приведения типов при получении значений.GenericBox.java: Теперь Create современную версию с проверкой типов, используя обобщения. Ваш GenericBox должен использовать параметр типаTв своём объявлении. Ему нужно private полеcontentтипаT, конструктор, который accepts значение типаT, и методgetContent(), который возвращает типT. При использовании этого класса приведение типов не needed!Main.java: Продемонстрируйте оба подхода рядом. Вы получите два входных значения: слово (String) и число (целое число).Сначала используйте старый подход: Create OldBox, хранящий слово, затем получите его с приведением к String и выведите:
OldBox (with cast): [value]Затем используйте подход с обобщениями: Create
GenericBox<String>, хранящий то же слово, получите его без приведения типов и выведите:GenericBox (no cast): [value]Затем покажите работу обобщений с целыми числами: Create
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, хранящий слово
// Retrieve with cast to String and print: "OldBox (with cast): [value]"
// TODO: Создайте GenericBox<String>, хранящий слово
// Retrieve without casting and print: "GenericBox (no cast): [value]"
// TODO: Create a GenericBox<Integer> storing the number
// Retrieve and print: "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