ジェネリクス(備忘録)

#・ジェネリクスとは？
・ジェネリクスとは、Swift実践入門（p.236）によると、型をパラメータとして受け取ることで汎用的なプログラムを記述するための機能である。これにより、関数や型を汎用的かつ型安全に記述できる。


#・定義方法
・ジェネリック関数の場合

```swift
func 関数名<型引数>(引数: 型引数) -> 戻り値の型 {
   関数呼び出し時に実行される文
}
```

・ジェネリック型（型引数をもつクラス、構造体、列挙型）の場合

```swift
struct 構造体名<型引数> {
    構造体の定義
}
```

型引数として宣言された方は、ジェネリック関数やジェネリック型の内部で通常の型と同等に扱うことができる。

#・特殊化
実際にジェネリック関数を呼び出したり、ジェネリック型をインスタンス化するときには、型引数に具体的な型を指定する必要があり、具体的な型引数を与えて型を確定させることを特殊化という。

配列にString型やInt型を当てはめて使用することができるのも特殊化のひとつである。

```swift 
var intArray = [1,2,3,]

var stringArray = ["a","b","c"]
```

特殊化の方法はジェネリック関数とジェネリック型それぞれについて大きく分けて二つある。

####・ジェネリック関数の場合
①引数からの型推論を用いる方法

```swift
func someFunc<T>(_ argument: T) -> T {
    return argument
}

let someString = someFunc("Hello") //String型
let someInt = someFunc(100) //Int型
```

②戻り値からの型推論を用いる方法

戻り値からの型推論で特殊化を行う際には、ジェネリック関数の戻り値の型が型引数となっていて、かつ、戻り値の代入先の型が決まっている必要がある。

```swift
func someFunc<T>(_ argument: Any) -> T? {
    return argument as? T
}

let someString: String? = someFunc("Hello") //TはString型となる

let error = someFunc("Error") //エラー
```

####・ジェネリック型の場合
①<>内に型引数を明示する方法

```swift
struct SomeStringStruct<Some> {
    let name : Some
}

let someString = SomeStringStruct<String>(name: "okadai") //String型
```

②型推論によって型引数を推論する方法

```swift 
struct SomeIntStruct<Some> {
    let num : Some
}

let someInt = SomeIntStruct(num: 100) //Int型
```

#・型制約
型制約により、準拠すべきプロトコルやスーパークラスなど、型引数にはさまざまな制約を設けることができる。これにより、型の性質を利用でき、ジェネリック関数やジェネリック型をより詳細に扱うことができる。

####・ジェネリック関数の場合
設けることができる制約

①スーパークラスや準拠するプロトコルに対する制約

```swift 
func 関数名<型引数: プロトコル名やスーパークラス名>(引数) {
    関数呼び出し時に実行される文
}
```

```swift 
func isEqual<T: Equatable>(_ x: T, _ y: T) -> Bool {
    return x == y
}

isEqual("abc", "def") // false
```

②連想型のスーパークラスや準拠するプロトコルに対する制約

```swift
func 関数名<型引数: プロトコル>(引数) -> 戻り値の型
    where 連想型: プロトコルやスーパークラス {
    関数呼び出し時に実行される文
}
```

```swift
func sorted<T: Collection>(_ argument: T) -> [T.Element]
where T.Element: Comparable {
    return argument.sorted()
}

sorted([3,2,1])
```

③型どうしの一致を要求する制約

```swift 
func 関数名<型引数1: プロトコル1, 型引数2: プロトコル2>(引数) -> 戻り値の型
where プロトコル1の連想型 == プロトコル2の連想型 {
    関数呼び出し時に実行される文
}
```

```swift
//この場合は、要素の型の一致を要求している
func concat<T: Collection, U: Collection>(_ argument1: T, _ argument2: U) -> [T.Element]
where T.Element == U.Element {
    return Array(argument1) + Array(argument2)
}

let array = [1,2,3]
let set = Set([1,2,3])
let result = concat(array, set) //[1,2,3,1,2,3]
```

####・ジェネリック型の場合
設けることができる制約

①スーパークラスや準拠するプロトコルに対する制約

```swift 
struct 型名<型引数: プロトコル名やスーパークラス名> {
    構造体の定義
}
```

####・ジェネリック型の型制約付きエクステンション

型の定義では、①スーパークラスや準拠するプロトコルに対する制約しか使用できなかったが、エクステンションでは、②連想型のスーパークラスや準拠するプロトコルに対する制約、③型どうしの一致を要求する制約も使えるようになる。

```swift
extension ジェネリック型名 where 型制約 {
    制約を満たす場合に有効となるエクステンション
}
```

```swift
struct Pair<Element> {
    let first: Element
    let second: Element
}

extension Pair where Element == String {
    func hasElement(containing character: Character) -> Bool {
        return first.contains(character) || second.contains(character)
    }
}

let stringPair = Pair(first: "abc", second: "def")
stringPair.hasElement(containing: "e") // true
```

####・プロトコルへの条件付き準拠
・ジェネリック型の型制約付きエクステンションでは、プロトコルへの準拠も可能。

```swift
extension ジェネリック型名: 条件付き準拠するプロトコル名 where 型制約 {
    制約を満たす場合に有効となるエクステンション
}
```

```swift 
struct Pair<Element> {
    let first: Element
    let second: Element
}

extension Pair: Equatable where Element: Equatable {
    static func == (_ lhs: Pair, _ rhs: Pair) -> Bool {
        return lhs.first == rhs.first && lhs.first == rhs.second
    }
}

let stringPair1 = Pair(first: "abc", second: "def")
let strinfPair2 = Pair(first: "def", second: "ghi")
let stringPair3 = Pair(first: "abc", second: "def")

stringPair1 == strinfPair2 // false
stringPair1 == stringPair3 // true
```