Видеокурсы по изучению языка программирования Swift. Подробнее

Параметры и аргументы универсального типа

Если вы нашли опечатку в тексте, выделите ее и нажмите CTRL + ENTER.

В этой главе описываются параметры и аргументы для функций и инициализаторов универсального типа. При объявлении функции или инициализатора универсального типа, вам необходимо указать параметры типа, с которым сможет работать эта функция функция или инициализатор. Эти параметры типа действуют в качестве плейсхолдера, которые заменяются реальными аргументами конкретного типа, когда создается экземпляр универсального типа или вызывается универсальная функция или инициализатор.

Подробнее см. Универсальные шаблоны.

Условие универсального параметра (Generic Parameter Clause)

Generic Parameter Clause определяет параметры типа универсального типа или функции, наряду с любыми связанными ограничениями и требованиями для этих параметров. Условие универсального параметра заключается в угловые скобки (<>) и имеет одну из следующих форм:

<список параметров универсального типа>

Список универсальных параметров является разделенным запятыми списком универсальных параметров, каждый из которых имеет следующий вид:

параметра типа: ограничение

Универсальный параметр состоит из параметра типа, за которым следует опциональное ограничение. Параметр типа - это просто имя типа плейсхолдера (например, T, U, V, Key, Value, и так далее). У вас есть доступ к параметрам типа (и к любому из их связанных типов) в остальной части типа, функциям или объявлению инициализатора, в том числе и в подписи функции или инициализатора.

Констрейнт указывает, что параметр типа наследует от определенного класса или соответствует протоколу или композиции протокола. Например, в универсальной функции ниже, универсальный параметр T: Comparable указывает на то, что любой тип аргументов, заменяемый параметром типа T, должен соответствовать протоколу Comparable.

func simpleMax<T: Comparable>(_ x: T, _ y: T) -> T {
   if x < y {
      return y
   }
   return x
}

Так как Int и Double, например, оба соответвуют протоколу Comparable, эта функция принимает аргументы любого типа. В отличие от универсальных типов, вам не нужно указывать условие универсального аргумента при использовании универсальной функции или инициализатора. Аргументы типа же вместо этого выводятся из типа аргументов, переданных функции или инициализатору.

simpleMax(17, 42) // T определеятеся как Int
simpleMax(3.14159, 2.71828) // T определяется как Double

Условие Where

Вы можете указать дополнительные требования к параметрам типа и их связанным типам, включая условие where после универсального списка параметров. Условие where состоит из ключевого слова where,за которым идет разделенный запятыми список из одного или нескольких требований.

where требования

Требования в условии where указывают, что параметр типа наследует от класса или соответствует протоколу или композиции протокола. Несмотря на то, что условие where обеспечивает синтаксический сахар для выражения простых ограничений на параметры типа (например, T: Comparable эквивалентно <T> where T: Comparable и так далее), вы можете использовать его для обеспечения более сложных ограничений параметров типа и их связанных типов. Например, where S.Iterator.Element: Equatable указывает на то, что S соответствует протоколу Sequence и что связанный тип S.Iterator.Element соответствует протоколу Equatable. Данное ограничение проверяет, чтобы каждый элемент последовательности можно было сравнить.

Вы можете также указать требование, что два типа должны быть идентичны, с помощью оператора == . Например, условие универсального параметра <S1: Sequence, S2: Sequence where S1.Iterator.Element == S2.Iterator.Element> выражает ограничения, где S1 и S2 должны соответствовать протоколу Sequence и что элементы обеих последовательностей должны быть одного и того же типа.

Любой аргумент типа, заменяемый параметром типа, должны соответствовать всем ограничениям и требованиям, предъявляемым к параметру типа.

Вы можете перегружать универсальную функцию или инициализатор, предоставляя различные ограничения, требования или все вместе, на параметры типа в условии универсального параметра. Когда вы вызываете перегруженную универсальную функцию или инициализатор, компилятор использует эти ограничения для решения того, какие перегруженные функции или инициализаторы вызвать.

Грамматика условия универсального параметра

generic-parameter-clause → generic-parameter-list­
generic-parameter-list → generic-parameter­  | generic-parameter­generic-parameter-list­
generic-parameter → type-name­
generic-parameter → type-name­type-identifier­
generic-parameter → type-name­protocol-composition-type­
generic-where-clause → where­requirement-list­
requirement-list → requirement­  | requirement­requirement-list­
requirement → conformance-requirement­  | same-type-requirement­
conformance-requirement → type-identifier­ : ­type-identifier­
conformance-requirement → type-identifier­ :­ protocol-composition-type­
same-type-requirement → type-identifier­ ==­ type­

Условие универсального аргумента

Условие универсального аргумента определяет аргументы типа универсального типа. Условие универсального аргумента заключается в угловые скобки (<>) и имеет следующий вид:

<список универсальных аргументов>

Список универсальных аргументов - это разделенный запятыми список аргументов типа. Тип аргумента - это имя фактического конкретного типа, который заменяет соответствующий параметр типа в условии универсального аргумента. Результатом является специализированная версия этого универсального типа. В качестве примера, стандартная библиотека Swift определяет универсальный словарь типа вот так:

struct Dictionary<Key: Hashable, Value>: Collection, ExpressibleByDictionaryLiteral {
    /* ... */
}

Специализированная версия универсального типа DictionaryDictionary<String, Int>  формируется через замену универсальных параметров Key: Hashable и Value конкретными аргументами типа String и Int. Каждый аргумент типа должен удовлетворять всем ограничениями универсального параметра, которые он заменяет, в том числе дополнительным требованиям, указанным в условии where. В приведенном выше примере параметр типа Key ограничен для соответствия протоколу Hashable и, следовательно, String должна также соответствовать протоколу Hashable.

Вы можете также заменить параметр типа аргументом типа, который сам по себе является специализированной версией универсального типа (при том условии, что он удовлетворяет соответствующим ограничениям и требованиям). Например, вы можете заменить параметр типа Element в Array<Element> специализированной версией массива Array<Int>, чтобы сформировать массив, элементы которого сами являются массивами целых чисел.

let arrayOfArrays: Array<Array<Int>> = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]

Как уже упоминалось в "Условие универсального параметра", вы не используете универсального условия аргумента для определения аргументов типа универсальной функции или инициализатора.

Грамматика условия универсального аргумента

generic-argument-clause <­ generic-argument-list 
generic-argument-list generic-argument | generic-argument,­ generic-argument-list
generic-argument type

Swift: 
3.0