Swift ジェネリック


Swift は、柔軟で再利用可能な関数と型を作成できるジェネリックスを提供します。

Swift 標準ライブラリは汎用コードで構築されています。

Swift の配列型と辞書型は両方とも汎用セットです。

Int 配列、String 配列、またはその他の Swift タイプのデータ配列を作成できます。

次の例は、2 つの Int 値を交換するために使用される非ジェネリック関数交換です:

// 定义一个交换两个变量的函数
func exchange(inout a: Int, inout b: Int) {
    let temp = a
    a = b
    b = temp
}

var numb1 = 100
var numb2 = 200

print("交换前数据: \(numb1) 和 \(numb2)")
exchange(&numb1, b: &numb2)
print("交换后数据: \(numb1) 和 \(numb2)")

上記のプログラム実行の出力結果は次のとおりです:

交换前数据: 100 和 200
交换后数据: 200 和 100

ジェネリック関数は、Int や String などの任意の型にアクセスできます。

次の例は、2 つの Int 値と String 値を交換するために使用される汎用関数交換です:

func exchange<T>(inout a: T, inout b: T) {
    let temp = a
    a = b
    b = temp
}

var numb1 = 100
var numb2 = 200

print("交换前数据:  \(numb1) 和 \(numb2)")
exchange(&numb1, b: &numb2)
print("交换后数据: \(numb1) 和 \(numb2)")

var str1 = "A"
var str2 = "B"

print("交换前数据:  \(str1) 和 \(str2)")
exchange(&str1, b: &str2)
print("交换后数据: \(str1) 和 \(str2)")

上記のプログラムの実行出力は次のとおりです:

交换前数据:  100 和 200
交换后数据: 200 和 100
交换前数据:  A 和 B
交换后数据: B 和 A

この関数の汎用バージョンは、プレースホルダー型名を使用します (通常、この場合実際の型名 (Int、String、Double など) の代わりに文字 T) で表されます。プレースホルダー型名は、型 T がどのようなものであるべきかを示唆するものではありませんが、型 T が何を表すかに関係なく、a と b が同じ型 T でなければならないことを示唆しています。 Exchange(_:_:) 関数が呼び出されるたびに渡される実際の型のみが、T が表す型を決定できます。

もう 1 つの違いは、このジェネリック関数名が続くプレースホルダー型名 (T) が山かっこで囲まれていることです (


ジェネリック型

Swift では、独自のジェネリック型を定義できます。

カスタム クラス、構造体、列挙型) Array や Dictionary を使用するのと同じように、任意の型で動作します

struct TOS<T> {
    var items = [T]()
    mutating func push(item: T) {
        items.append(item)
    }
    
    mutating func pop() -> T {
        return items.removeLast()
    }
}

var tos = TOS<String>()
tos.push("Swift")
print(tos.items)

tos.push("泛型")
print(tos.items)

tos.push("类型参数")
print(tos.items)

tos.push("类型参数名")
print(tos.items)


let deletetos = tos.pop()

上記のプログラム実行の出力結果は次のとおりです:

["Swift"]
["Swift", "泛型"]
["Swift", "泛型", "类型参数"]
["Swift", "泛型", "类型参数", "类型参数名"]

拡張ジェネリック型

(拡張キーワードを使用して) ジェネリック型を拡張すると、次のようになります。拡張機能の定義で型パラメータ リストを指定する必要はありません。元の型定義で宣言された型パラメータ リストが拡張機能で使用でき、これらのパラメータ名は参照として使用されます。

次の例では、first という名前の読み取り専用の計算プロパティを追加して、スタックから削除せずに最上位の要素を返す汎用 TOS タイプを拡張します。

struct TOS<T> {
    var items = [T]()
    mutating func push(item: T) {
        items.append(item)
    }
    
    mutating func pop() -> T {
        return items.removeLast()
    }
}

var tos = TOS<String>()
tos.push("Swift")
print(tos.items)

tos.push("泛型")
    print(tos.items)
    
    tos.push("类型参数")
    print(tos.items)
    
    tos.push("类型参数名")
    print(tos.items)

// 扩展泛型 TOS 类型
extension TOS {
    var first: T? {
        return items.isEmpty ? nil : items[items.count - 1]
    }
}

if let first = tos.first {
    print("栈顶部项:\(first)")
}

上記のプログラム実行の出力結果は次のとおりです:

["Swift"]
["Swift", "泛型"]
["Swift", "泛型", "类型参数"]
["Swift", "泛型", "类型参数", "类型参数名"]
栈顶部项:类型参数名

型制約

型制約は、指定されたクラスから継承するか、特定のプロトコルまたはプロトコル コンポーネントに従う必要がある型パラメーターを指定します。

ジェネリック関数に作用する型パラメータ チェーンの一部として、型パラメータ名の後にコロンで区切って型制約を記述することができます。制約の基本構文は次のとおりです (ジェネリック型の構文と同じです)。 :

func someFunction<T: SomeClass, U: SomeProtocol>(someT: T, someU: U) {
    // 这里是函数主体
}

Instance

// 函数可以作用于查找一字符串数组中的某个字符串
func findStringIndex(array: [String], _ valueToFind: String) -> Int? {
    for (index, value) in array.enumerate() {
        if value == valueToFind {
            return index
        }
    }
    return nil
}

let strings = ["cat", "dog", "llama", "parakeet", "terrapin"]
if let foundIndex = findStringIndex(strings, "llama") {
    print("llama 的下标索引值为 \(foundIndex)")
}

上記のプログラム実行出力は次のとおりです:

llama 的下标索引值为 2

関連付けられた型インスタンス

Swift で typealias キーワードを使用して関連付けられた型を設定します


プロトコルを定義するとき、プロトコルを宣言すると便利な場合があります。

protocol Container {
    // 定义了一个ItemType关联类型
    typealias ItemType
    mutating func append(item: ItemType)
    var count: Int { get }
    subscript(i: Int) -> ItemType { get }
}

// 遵循Container协议的泛型TOS类型
struct TOS<T>: Container {
    // original Stack<T> implementation
    var items = [T]()
    mutating func push(item: T) {
        items.append(item)
    }
    
    mutating func pop() -> T {
        return items.removeLast()
    }
    
    // conformance to the Container protocol
    mutating func append(item: T) {
        self.push(item)
    }
    
    var count: Int {
        return items.count
    }
    
    subscript(i: Int) -> T {
        return items[i]
    }
}

var tos = TOS<String>()
tos.push("Swift")
print(tos.items)

tos.push("泛型")
print(tos.items)

tos.push("参数类型")
print(tos.items)

tos.push("类型参数名")
print(tos.items)

上記のプログラム実行の出力は次のとおりです:

["Swift"]
["Swift", "泛型"]
["Swift", "泛型", "参数类型"]
["Swift", "泛型", "参数类型", "类型参数名"]

Where ステートメント

型制約により、型がジェネリック関数またはクラスの定義制約に準拠していることが保証されます。

パラメータリストの where ステートメントを通じてパラメータ制約を定義できます。

型パラメーター リストの直後に where ステートメントを記述し、その後に関連する型に対する 1 つ以上の制約、および/または型と関連する型の間の 1 つ以上の等価性を記述できます。

次の例では、allItemsMatch という汎用関数を定義し、2 つの Container インスタンスに同じ要素が同じ順序で含まれているかどうかを確認します。

すべての要素が一致する場合は、ブール値 true が返され、それ以外の場合は false が返されます。

protocol Container {
    typealias ItemType
    mutating func append(item: ItemType)
    var count: Int { get }
    subscript(i: Int) -> ItemType { get }
}

struct Stack<T>: Container {
    // original Stack<T> implementation
    var items = [T]()
    mutating func push(item: T) {
        items.append(item)
    }
    
    mutating func pop() -> T {
        return items.removeLast()
    }
    
    // conformance to the Container protocol
    mutating func append(item: T) {
        self.push(item)
    }
    
    var count: Int {
        return items.count
    }
    
    subscript(i: Int) -> T {
        return items[i]
    }
}

func allItemsMatch<
    C1: Container, C2: Container
    where C1.ItemType == C2.ItemType, C1.ItemType: Equatable>
    (someContainer: C1, anotherContainer: C2) -> Bool {
        // 检查两个Container的元素个数是否相同
        if someContainer.count != anotherContainer.count {
            return false
        }
        
        // 检查两个Container相应位置的元素彼此是否相等
        for i in 0..<someContainer.count {
            if someContainer[i] != anotherContainer[i] {
                return false
            }
        }
        // 匹配所有项,返回 true
        return true
}

var tos = Stack<String>()
tos.push("Swift")
print(tos.items)

tos.push("泛型")
print(tos.items)

tos.push("Where 语句")
print(tos.items)

var eos = ["Swift", "泛型", "Where 语句"]
print(eos)

上記のプログラムの実行の出力結果は次のとおりです:

["Swift"]
["Swift", "泛型"]
["Swift", "泛型", "Where 语句"]
["Swift", "泛型", "Where 语句"]