Обобщенные типы
Обобщенные типы можно рассматривать как универсальные шаблоны, параметризуемые типами. Обобщенные типы позволяют избежать жесткого определения используемых типов. Если данные и/или алгоритмы применяются для различных типов, использование обобщенных типов позволяет:
- избежать повторного написания программного кода;
- реализовывать более универсальные алгоритмы.
Синтаксис
Синтаксис объявления обобщенного типа:
имя-типа<имя-типа-параметра, ..., имя-типа-параметра>
- имя-типа — имя обобщенного типа;
- имя-типа-параметра — параметры обобщенного типа.
Параметры можно указывать в именованной форме:
имя-типа<имя-параметра1 = Тип1, ..., имя-параметраN = ТипN>
Например:
Соответствие<Число, ТипЗначения = Строка>
Ограничения:
- в именованной форме нельзя указать параметры функциональных типов;
- именованные параметры должны следовать строго за позиционными (при их наличии).
Возможные ограничения на типы-параметры
На типы-параметры (переменные типа в объявлении, например, Т в Массив<Т>) некоторых обобщенных типов могут накладываться ограничения. Всего есть два типа ограничений:
- Ограничение на иерархию — тип-специализация (конкретный тип, подставляемый вместо параметра, например,
ЧисловМассив<Число>) должен иметь определенный базовый тип. - Ограничение на наличие значения по умолчанию — тип-специализация должен иметь значение по умолчанию.
При попытке объявить тип с неподходящим типом-специализацией возни кает ошибка компиляции.
Информация времени выполнения
Информация о типах-параметрах обобщенного типа сохраняется до времени выполнения. При проверке типа с помощью операции это данная информация учитывается. Пример:
метод Обработать()
пер СоставнойМассив = [10, "20", 30, 40] // Массив<Число|Строка>
пер МассивСтрок = ["10", "20", "30", "40"] // Массив<Строка>
пер МассивЧисел = [10, 20, 30, 40] // Массив<Число>
пер Сравнение: Булево = СоставнойМассив это Массив<Строка|Число> // Истина
для Индекс = 0 по СоставнойМассив.Размер() - 1
если СоставнойМассив[Индекс] это Число
СоставнойМассив[Индекс] = СоставнойМассив[Индекс].ВСтроку()
;
;
// СоставнойМассив содержит только строки, но его составной тип остался Массив<Строка|Число>
Сравнение = СоставнойМассив == МассивСтрок // Истина (сравнение по содержимому)
Сравнение = СоставнойМассив == МассивЧисел // Ложь (разное содержимое)
Сравнение = СоставнойМассив это Массив<Строка|Число> // Истина (параметры обобщенного типа не изменились)
Сравнение = СоставнойМассив это Массив<Строка> // Несовместимые типы Массив<Число|Строка> и Массив<Строка>
;
Сравнен ие экземпляров обобщенных типов
Сравнение экземпляров обобщенных типов не учитывает параметры обобщенного типа. Например:
пер СоответствиеОбъект = <Объект, Объект>{1 : "один", 2 : "два"}
пер СоответствиеЧисло = <Число, Строка>{1 : "один", 2 : "два"}
пер Сравнение = СоответствиеОбъект == СоответствиеЧисло // Истина
Типизированные литералы коллекций
Обобщенные типы можно указывать для литералов коллекций. Это позволяет явно задавать типы элементов при создании массивов, множеств и соответствий. Примеры с нач альными значениями:
пер Массив = <Число>[1, 2]
пер Множество = <Число>{1, 2}
пер Соответствие = <Число, Строка>{1 : "один", 2 : "два"}
Для создания пустых коллекций с предопределенным типом элементов используется следующий синтаксис:
пер ПустойМассив = <Число>[]
пер ПустоеМножество = <Число>{}
пер ПустоеСоответствие = <Число, Строка>{:}
Тип литералов коллекций определяется по следующим правилам:
- Если тип указан явно в параметрах обобщенного типа (например,
<Число>), используется указанный тип. - Если тип не указан явно, но литерал присваивается переменной с известным типом, используется тип переменной.
- Если ни одно из вышеперечисленных условий не выполняется, тип элементов определяется как объединение типов всех элементов коллекции.
Например:
пер ЧитаемыйМассивЧисел: Коллекция<Число> = [1] // Тип: ЧитаемыйМассив<Число>
пер Массив = [1, Истина] // Тип: Массив<Число|Булево>
пер МассивНаследников: Коллекция<Объе кт> = [1, Истина, "Строка"] // Тип: ЧитаемыйМассив<Число|Булево|Строка>
Специализированные методы обобщенных типов
Специализированные методы обобщенных типов — это методы, которые становятся доступны только при определенных условиях. Эти условия связаны с иерархией типов: метод становится доступен, когда тип-параметр (или типы-параметры) удовлетворяет заданным требованиям наследования.
Рассмотрим пример:
// Возвращает максимальное значение в коллекции
ЧитаемаяКоллекция<Т это Сравнимое<Т>>.Максимум()
В данном примере Т это Сравнимое<Т> является условием доступности метода. Метод Максимум() может быть применен к обобщенному типу ЧитаемаяКоллекция, только если в параметре типа установлен тип, унаследованный от Сравнимое<Т>:
метод ВычислитьМаксимум()
пер КоллекцияБайты = <Байты>[Байты{AAFF}, Байты{AAAA}, Байты{FFFF}]
пер ОбходимаяКоллекция = <Число>[10, 60, 42]
// Вызов метода типа Обходимое
пер МаксЧисло = ОбходимаяКоллекция.Максимум()
// Тип-параметр "ТипЭлемента" не удовлетворяет ограничению "Сравнимое<ItemType>"
пер МаксБайты = КоллекцияБайты.Максимум()
;
Иерархия типов и вариантность
Обобщенные типы, не допускающие запись, ковариантны по типу и по параметрам.
-
Если тип
Дочернийявляется подтипом типаБазовый, то обобщенный типимя-типа<Дочерний>можно присвоить переменной типаимя-типа<Базовый>, но не наоборот:пер Базовый = новый ЧитаемыйМассив<Объект>([])
пер Дочерний = новый ЧитаемыйМассив<Строка>([])
Базовый = Дочерний
Дочерний = Базовый -
Если тип
Дочернийявляется подтипом типаБазовый, то обобщенный типДочерний<имя-параметра>можно присвоить переменной типаБазовый<имя-параметра>, но не наоборот:пер Базовый = новый ЧитаемыйМассив<Строка>([])
пер Дочерний = новый Массив<Строка>()
Базовый = Дочерний
Дочерний = Базовый -
Если верны оба утверждения, описанных выше:
пер Базовый = новый ЧитаемыйМассив<Объект>([])
пер Дочерний = новый Массив<Строка>()
Базовый = Дочерний
Дочерний = Базовый
Обобщенные типы, допускающие запись объектов, не ковариантны (например, тип Массив<>).
пер Базовый = новый Массив<Объект>()
пер Дочерний = новый Массив<Строка>()
Базовый = Дочерний