ジェネリックパラメータと引数(Generic Parameters and Arguments)
最終更新日: 2023/8/11 原文: https://docs.swift.org/swift-book/ReferenceManual/GenericParametersAndArguments.html
宣言を一般化して具体的な型を抽象化する。
この章では、ジェネリック型、関数、およびイニシャライザのパラメータと引数について説明します。ジェネリック型、関数、サブスクリプト、またはイニシャライザを宣言すると、ジェネリック型、関数、またはイニシャライザで使用できる型パラメータを指定します。これらの型パラメータは、ジェネリック型のインスタンスが作成されたとき、またはジェネリック関数またはイニシャライザが呼び出されたときに、実際の具象型の引数に置き換えられるプレースホルダとして機能します。
Swift のジェネリクスの概要については、Generics(ジェネリクス)を参照ください。
ジェネリックパラメータ句(Generic Parameter Clause)
ジェネリックパラメータ句は、ジェネリック型または関数の型パラメータを指定し、それらのパラメータに関連する制約や要件とともに指定します。ジェネリックパラメータ句は、山括弧(<>)で囲まれており、形式は次のとおりです。
<<#generic parameter list#>>
generic parameter list は、ジェネリックパラメータのカンマ区切りのリストで、その各形式は次のとおりです。
<#type parameter#>: <#constraint#>
type parameter の後に任意の constraint を付けて構成します。type parameter は、単にプレースホルダ型の名前です(例えば、T、U、V、Key、Value など)。関数またはイニシャライザのシグネチャを含む型や関数の残りの部分から型パラメータ(およびその関連型の全ての型)にアクセスできます。
constraint は、型パラメータが特定のクラスを継承するか、プロトコルまたはプロトコル合成に準拠することを指定します。例えば、下記のジェネリック関数では、ジェネリックパラメータ 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 に推論されます
整数ジェネリックパラメータ(Integer Generic Parameters)
整数のジェネリックパラメータは、型ではなく整数値のプレースホルダーとして機能します。その形は以下の通りです。
let <#type parameter#>: <#type>
型は、Swift 標準ライブラリの Int 型、または Int に解決される型エイリアスまたはジェネリック型でなければなりません。
整数のジェネリックパラメータに提供する値は、整数リテラルまたはそれを含めたジェネリックコンテキストからの別の整数ジェネリックパラメータでなければなりません。例えば、
struct SomeStruct<let x: Int> { }
let a: SomeStruct<2> // OK: 整数リテラル
struct AnotherStruct<let x: Int, T, each U> {
let b: SomeStruct<x> // OK: 他の整数ジェネリックパラメータ
static let c = 42
let d: SomeStruct<c> // Error: 定数は使えない
let e: SomeStruct<T> // Error: ジェネリック型パラメータは使えない
let f: SomeStruct<U> // Error: パラメータパックは使えない
}
型の整数ジェネリックパラメータの値は、その型の静的定数メンバとしてアクセス可能であり、型自体と同じ可視性を持ちます。関数の整数ジェネリックパラメータの値は、関数内から定数としてアクセス可能です。これらの定数は式で使用されると、Int 型として扱われます。
print(a.x) // "2"を出力
整数のジェネリックパラメータの値は、型を初期化したり関数を呼び出したりするときに、使用する引数の型から推論できます。
struct AnotherStruct<let y: Int> {
var s: SomeStruct<y>
}
func someFunction<let z: Int>(s: SomeStruct<z>) {
print(z)
}
let s1 = SomeStruct<12>()
let s2 = AnotherStruct(s: s1) // AnotherStruct.y は 12 に推論される
someFunction(s: s1) // "12"を出力
ジェネリック where 句(Generic Where Clauses)
型または関数の本文の開始中括弧({)の直前にジェネリック where 句を含むことによって、型パラメータとその関連型に追加要件を指定できます。ジェネリック where 句は、where キーワードで構成され、その後に 1 つ以上の要件のカンマ区切りのリストが続きます。
where <#requirements#>
ジェネリック where 句の requirements は、型パラメータがクラスを継承するか、プロトコルまたはプロトコル合成に準拠することを指定します。ジェネリック where 句は型パラメータのシンプルな制約を表現するための糖衣構文(シンタックスシュガー)を提供しますが(例えば、<T: Comparable> は <T> where T: Comparable などと同等)、それを使用して複雑な制約を型パラメータや関連型に提供することもできます。例えば、プロトコルに準拠するために、関連型の型パラメータを制限することができます。<S: Sequence> where S.Iterator.Element: Equatable は、S が Sequence プロトコルに準拠し、関連型 S.Iterator.Element が Equatable プロトコルに準拠することを指定します。この制約は、シーケンスの各要素が Equatable だということを保証します。
整数のジェネリックパラメータは、プロトコルまたはスーパークラスの要件を持つことはできません。
== 演算子を使用して、2 つの型が同じという要件を指定することもできます。例えば、<S1: Sequence, S2: Sequence> where S1.Iterator.Element == S2.Iterator.Element は S1 と S2 が Sequence プロトコルに準拠し、両方のシーケンスの要素が同じ型でなければならない制約を表現します。
整数のジェネリックパラメータの場合、== 演算子はその値に対する要件を指定します。これにより、2 つの整数ジェネリックパラメータが同じ値を持つことを要求したり、整数ジェネリックパラメータに特定の整数値を要求したりすることができます。
型パラメータに置き換えられる任意の型引数は、型パラメータの全て制約と要件を満たす必要があります。
ジェネリック where 句は、型パラメータを含む宣言の一部として、または型パラメータを含む宣言の中にネストされている宣言の一部として使用することができます。ネストされた宣言のジェネリック where 句は、その宣言をラップしている宣言の型パラメータを参照できます。ただし、その要件は、where 句が書かれている宣言にのみ適用されます。
ラップされている宣言にも where 句がある場合、両方の句の要件が組み合わされます。下記の例では、startSwithZero() は、Element が SomeProtocol と Numeric の両方に準拠している場合にのみ使用できます。
extension Collection where Element: SomeProtocol {
func startsWithZero() -> Bool where Element: Numeric {
return first == .zero
}
}
異なる制約、要件、またはその両方を提供することによって、ジェネリック関数またはイニシャライザをオーバーロードすることができます。オーバーロードされたジェネリック関数またはイニシャライザを呼び出すと、コンパイラはこれらの制約を使用して、どのオーバーロードされた関数またはイニシャライザを呼び出すかを決めます。
ジェネリック where 句の詳しい情報とジェネリック関数宣言の例は、Generic Where Clauses(ジェネリック where 句)を参照ください。
Grammar of a generic parameter clause:
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-parameter →lettype-name:type \generic-where-clause →
whererequirement-list \ requirement-list → requirement | requirement,requirement-list \ requirement → conformance-requirement | same-type-requirementconformance-requirement → type-identifier
:type-identifier \ conformance-requirement → type-identifier:protocol-composition-type \ same-type-requirement → type-identifier==type same-type-requirement → type-identifier==signed-integer-literal
ジェネリック引数句(Generic Argument Clause)
ジェネリック引数句は、ジェネリック型の型引数を指定します。ジェネリック引数句は山括弧(<>)で囲まれており、形式は次のとおりです。
<<#generic argument list#>>
generic argument list は、型引数のカンマ区切りのリストです。型引数は、ジェネリック型のジェネリックパラメータ句に対応する型パラメータを置き換えた実際の具象型の名前です。整数ジェネリックパラメータの場合、型引数はその整数ジェネリックパラメータを置き換える整数値になります。結果として、そのジェネリック型の特殊化されたバージョンになります。下記の例は、Swift 標準ライブラリのジェネリックな辞書型のシンプルなバージョンを示しています。
struct Dictionary<Key: Hashable, Value>: Collection, ExpressibleByDictionaryLiteral {
/* ... */
}
ジェネリック Dictionary 型の具象型バージョン、Dictionary<String, Int> は、ジェネリックパラメータ Key: Hashable と Value を具象型の String と Int に置き換えることで形成されます。各型引数は、それが置き換えるジェネリックパラメータの全ての制約を満たす必要があります。上記の例では、Key 型パラメータは Hashable プロトコルに準拠するように制約され、したがって String も Hashable プロトコルに準拠していなければなりません。
(適切な制約と要件を満たす場合)ジェネリック型の具象型バージョンで、型パラメータを型引数で置き換えることもできます。例えば、Array<Element> の型パラメータ Element を配列 Array<Int> に置き換えることができ、要素が整数の配列を形成することができます。
let arrayOfArrays: Array<Array<Int>> = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
Generic Parameter Clause(ジェネリックパラメータ句)で述べたように、ジェネリック関数またはイニシャライザの型引数を指定する場合には、ジェネリック引数句を使用しません。
Grammar of a generic argument clause:
generic-argument-clause →
<generic-argument-list>\ generic-argument-list → generic-argument | generic-argument,generic-argument-list \ generic-argument → type | signed-integer-literal