Всем привет!
В этом видео мы рассмотрим, как избежать дублирования кода для общей логики,
как отказаться от перегрузки методов,
функции и создавать типы данных, не зависящие от типизации.
Использование дженериков позволяет нам создавать функции и типы,
которые будут работать с любым типом данных в зависимости от наших требований.
Благодаря этому, мы можем выделить абстрактную логику и избежать
дублирование кода, написанного для использования с разными типами данных.
Дженерики не являются новшеством в программировании.
Мы можем встретить их в других языках, таких как C++, Java, C#, D.
Этот подход к программированию называется обобщённое программирование.
Представим, что нам необходимо реализовать функцию,
описывающую переданный аргумент в определённом формате.
Без использования дженериков нам пришлось бы перегружать
функцию для каждого типа данных.
Эти функции выполняют один и тот же код, не зависящий от типа данных,
поэтому, используя дженерики, мы можем написать единую реализацию для метода.
Функция используется PlaceholderType.
Что это такое?
Это псевдотип данных, так называемый Placeholder.
Мы сообщаем функции или методу, что она работает с чем-то, что придёт извне.
Так как логика не затрагивает методов и свойств определённого типа,
то нам и не важно в реализации функции, с каким типом данных мы работаем.
Вместо PlaceholderType может быть любой текст — t,
anytype — однако, лучше использовать информативные имена.
Название типа описывается в UpperCamelCase формате.
Объявления всех плейсхолдеров происходят сразу
после имени функции в треугольных скобках и через запятую.
Можно объявлять такое количество плейсхолдеров, которое вам будет нужно.
Кроме функций с дженерик-параметрами Swift позволяет
определять свои дженерик-типы данных.
Это могут быть классы, структуры или перечисления, которые будут
работать с любым типом данных аналогичным образом как массивы или словари.
На слайде показана структура, реализующая стек.
Реализация основывается на использовании массива, в котором хранятся значения.
В нашем примере мы жёстко типизировали контейнер для хранения Int.
Такая реализация позволяет работать нам только с одним типом данных.
Для работы со строками нам потребуется написать аналогичную структуру с
типизацией String или же использовать общий тип Any.
Использование Any — не лучшее решение в данном случае.
Каждый раз при работе с данными нам придётся использовать приведение типа.
Лучшим решением будет создание структуры с дженерик-параметрами.
Так как массивы Swift могут содержать любые типы данных одного типа,
то нам не важно, что будет на входе.
Мы сообщаем структуре об используемом типе данных с помощью ContainedType.
Теперь при обращении к стеку мы получаем типизированное значение,
которое можно использовать без приведения типа.
Есть и отрицательные моменты при использовании дженериков.
Например, снижение производительности во время исполнения.
Это связано с дополнительной обработкой кода, который будет сгенерирован.
Что не так важно при редком обращении к дженерик-коду,
но ощутимо при работе с большими модулями,
где дженерик-код используется повсеместно.
Например, стандартные библиотеки.
Для решения этой задачи был введён атрибут @_specialize, который
подсказывает оптимизатору, какие типы данных будут использоваться чаще всего.
Атрибут применяется только для функции.
Такое решение требует балансировки, времени компиляции,
размера бинарного файла и производительности в режиме исполнения.
Не стоит использовать данный атрибут повсеместно.
Также важно помнить,
что поддержка этого атрибута не гарантируется в следующих версиях языка.
Следующая лекция будет посвящена дженерик-констрейтам,
с помощью которых мы можем ограничивать дженерик-параметры различными условиями.