Перейти к основному содержимому

Обобщенные типы

Обобщенные типы можно рассматривать как универсальные шаблоны, параметризуемые типами. Обобщенные типы позволяют избежать жесткого определения используемых типов. Если данные и/или алгоритмы применяются для различных типов, использование обобщенных типов позволяет:

  • избежать повторного написания программного кода;
  • реализовывать более универсальные алгоритмы.

Синтаксис

Синтаксис объявления обобщенного типа:

имя-типа<имя-типа-параметра, ..., имя-типа-параметра>
  • имя-типа — имя обобщенного типа;
  • имя-типа-параметра — параметры обобщенного типа.

Параметры можно указывать в именованной форме:

имя-типа<имя-параметра1 = Тип1, ..., имя-параметраN = ТипN>

Например:

Соответствие<Число, ТипЗначения = Строка>

Ограничения:

  • в именованной форме нельзя указать параметры функциональных типов;
  • именованные параметры должны следовать строго за позиционными (при их наличии).

Возможные ограничения на типы-параметры

На типы-параметры (переменные типа в объявлении, например, Т в Массив<Т>) некоторых обобщенных типов могут накладываться ограничения. Всего есть два типа ограничений:

  1. Ограничение на иерархию — тип-специализация (конкретный тип, подставляемый вместо параметра, например, Число в Массив<Число>) должен иметь определенный базовый тип.
  2. Ограничение на наличие значения по умолчанию — тип-специализация должен иметь значение по умолчанию.

При попытке объявить тип с неподходящим типом-специализацией возникает ошибка компиляции.

Информация времени выполнения

Информация о типах-параметрах обобщенного типа сохраняется до времени выполнения. При проверке типа с помощью операции это данная информация учитывается. Пример:

метод Обработать()
пер СоставнойМассив = [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>"
пер МаксБайты = КоллекцияБайты.Максимум()
;

Иерархия типов и вариантность

Обобщенные типы, не допускающие запись, ковариантны по типу и по параметрам.

  • Если тип Дочерний является подтипом типа Базовый, то обобщенный тип имя-типа<Дочерний> можно присвоить переменной типа имя-типа<Базовый>, но не наоборот:

    пер Базовый = новый ЧитаемыйМассив<Объект>([])
    пер Дочерний = новый ЧитаемыйМассив<Строка>([])

    Базовый = Дочерний
    Дочерний = Базовый
  • Если тип Дочерний является подтипом типа Базовый, то обобщенный тип Дочерний<имя-параметра> можно присвоить переменной типа Базовый<имя-параметра>, но не наоборот:

    пер Базовый = новый ЧитаемыйМассив<Строка>([])
    пер Дочерний = новый Массив<Строка>()

    Базовый = Дочерний
    Дочерний = Базовый
  • Если верны оба утверждения, описанных выше:

    пер Базовый = новый ЧитаемыйМассив<Объект>([])
    пер Дочерний = новый Массив<Строка>()

    Базовый = Дочерний
    Дочерний = Базовый

Обобщенные типы, допускающие запись объектов, не ковариантны (например, тип Массив<>).

    пер Базовый = новый Массив<Объект>()
пер Дочерний = новый Массив<Строка>()

Базовый = Дочерний