Swift中的多型別約束
在Swift中,有多種方法可以對型別實現型別約束。我們將使用一些常見的方法,例如`where`子句、協議等。泛型提供了很大的靈活性,可以編寫更好、更安全的Swift程式碼。我們可以將泛型應用於集合、自定義型別等。其中之一是泛型型別約束。使用型別約束,您可以使泛型程式碼的行為符合您定義的特定約束集。
Swift提供了多種指定泛型型別引數型別約束的方法。
使用“where”子句進行型別約束
Swift中的“where”子句是一個非常強大的概念,用於對類、結構體、列舉等指定一個或多個型別約束。您可以使用“where”子句建立多種約束,以根據情況控制程式碼的行為。
示例1
查詢陣列所有元素的和
讓我們從一個基本示例開始,您必須找到陣列中所有元素的和。但是,您必須向方法新增約束才能對數字元素執行求和操作。要定義求和函式,我們將使用“where”子句建立一個帶有型別約束的擴充套件。我們將符合`Numeric`協議。程式碼如下:
import Foundation extension Array where Element: Numeric { func sum() -> Element { return reduce(0, +) } } let numbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] let sum = numbers.sum() print("Original array: \(numbers)") print("Sum is: \(sum)")
輸出
Original array: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] Sum is: 45
在上面的示例中,我們建立了一個擴充套件以及一個名為“sum”的方法。該函式應使用`reduce`函式計算所有數字元素的和。
示例2
檢查`Equatable`型別的相等元素
在這個例子中,我們將編寫一個函式來檢查元素是否相等。在這裡,我們將新增一個型別約束,即`Equatable`協議。程式碼如下:
import Foundation func printIfEqual<T: Equatable>(_ a: T, _ b: T) where T: CustomStringConvertible { if a == b { print("Values are equal: \(a)") } else { print("Values are not equal") } } let a = 10 let b = 10 printIfEqual(a, b) let c = "Hello" let d = "World" printIfEqual(c, d)
輸出
Values are equal: 10 Values are not equal
在上面的示例中,函式`printIfEqual`採用兩個泛型引數`a`和`b`,它們必須符合`Equatable`協議和`CustomStringConvertible`協議。`CustomStringConvertible`協議用於將值轉換為字串以便列印。
作為`printIfEqual`函式的一部分,我們首先使用`==`運算子檢查值是否相似。如果兩個值相等,則列印值;否則,列印這兩個值不相等。
該函式(例如`printIfEqual`)被呼叫兩次,一次使用整數值,一次使用字串值。由於整數值相等,該函式列印值。但是,字串值不相等,因此該函式列印一條訊息,指示這些值不相等。
使用協議進行型別約束
您還可以將協議指定為型別約束。當您想將型別引數限制為特定協議時,這很有用。
例如,您想定義一個通用協議,負責管理程式碼中不同型別的模型。我們定義了一個名為`ModelManager`的協議,它具有以下特性:
關聯的模型型別
一個獲取模型型別集合的函式,其中集合具有型別約束,即`Element`必須是模型型別的型別。
查詢型別,實現者可以使用任何想要表達查詢的內容——例如列舉。
import Foundation protocol ModelManager { associatedtype Model associatedtype Collection: Swift.Collection where Collection.Element == Model associatedtype Query func models(matching query: Query) -> Collection }
現在,有了上述內容,我們可以自由地實現所有提供完全相同API的模型管理器——但仍然可以使用與它們需求匹配的型別和集合。例如,要為`User`模型實現`ModelManager`,我們可以選擇使用`Array`作為我們的集合型別,以及一個允許我們按姓名或年齡匹配使用者的`Query`列舉:
struct User { let name: String let age: Int } struct Genre: Hashable { // declare properties and methods here } struct Movie { // declare properties and methods here } class UserManager: ModelManager { typealias Model = User enum Query { case name(String) case ageRange(Range) } func models(matching query: Query) -> [User] { // write complete code here return [] } }
對於其他模型,使用`Dictionary`作為集合型別可能更合適。這是另一個根據型別跟蹤電影並允許我們查詢匹配名稱或導演的電影的管理器:
class MovieManager: ModelManager { typealias Model = (key: Genre, value: Movie) enum Query { case name(String) case director(String) } func models(matching query: Query) -> [Genre : Movie] { // write complete code here return [:] } }
透過使用受約束的關聯型別,我們可以獲得面向協議程式設計的強大功能,並實現更輕鬆的模擬和可測試性,同時在實現具體型別時仍然具有很大的靈活性。
結論
Swift的泛型系統大量使用型別約束。它們使您可以將可與泛型函式或型別一起使用的型別限制為僅符合特定條件的那些型別。Swift有多種定義型別約束的方法,包括使用協議、類和`where`子句。透過使用型別約束,您可以開發更型別安全、可重用的程式碼,減少重複和錯誤。