欧美一区二区三区老妇人-欧美做爰猛烈大尺度电-99久久夜色精品国产亚洲a-亚洲福利视频一区二区

Swift的泛型特性是怎樣的

今天就跟大家聊聊有關(guān)Swift的泛型特性是怎樣的,可能很多人都不太了解,為了讓大家更加了解,小編給大家總結(jié)了以下內(nèi)容,希望大家根據(jù)這篇文章可以有所收獲。

曲水網(wǎng)站制作公司哪家好,找創(chuàng)新互聯(lián)!從網(wǎng)頁(yè)設(shè)計(jì)、網(wǎng)站建設(shè)、微信開(kāi)發(fā)、APP開(kāi)發(fā)、響應(yīng)式網(wǎng)站開(kāi)發(fā)等網(wǎng)站項(xiàng)目制作,到程序開(kāi)發(fā),運(yùn)營(yíng)維護(hù)。創(chuàng)新互聯(lián)自2013年起到現(xiàn)在10年的時(shí)間,我們擁有了豐富的建站經(jīng)驗(yàn)和運(yùn)維經(jīng)驗(yàn),來(lái)保證我們的工作的順利進(jìn)行。專注于網(wǎng)站建設(shè)就選創(chuàng)新互聯(lián)。

泛型代碼讓你能根據(jù)你所定義的要求寫(xiě)出可以用于任何類型的靈活的、可復(fù)用的函數(shù)。你可以編寫(xiě)出可復(fù)用、意圖表達(dá)清晰、抽象的代碼。

泛型是 Swift 最強(qiáng)大的特性之一,很多 Swift 標(biāo)準(zhǔn)庫(kù)是基于泛型代碼構(gòu)建的。實(shí)際上,甚至你都沒(méi)有意識(shí)到在語(yǔ)言指南中一直在使用泛型。例如,Swift  的 Array 和 Dictionary 類型都是泛型集合。

你可以創(chuàng)建一個(gè)容納 Int 值的數(shù)組,或者容納String 值的數(shù)組,甚至容納任何 Swift  可以創(chuàng)建的其他類型的數(shù)組。同樣,你可以創(chuàng)建一個(gè)存儲(chǔ)任何指定類型值的字典,而且類型沒(méi)有限制。

2. 泛型解決的問(wèn)題

下面的 swapTwoInts(_:_:) 是一個(gè)標(biāo)準(zhǔn)的非泛型函數(shù),用于交換兩個(gè) Int 值:

func swapTwoInts(_ a: inout Int, _ b: inout Int) {     let temporaryA = a     a = b     b = temporaryA }

如輸入輸出形式參數(shù)中描述的一樣,這個(gè)函數(shù)用輸入輸出形式參數(shù)來(lái)交換 a 和 b 的值。

swapTwoInts(_:_:) 函數(shù)把 b 原本的值給 a ,把 a 原本的值給 b 。你可以調(diào)用這個(gè)函數(shù)來(lái)交換兩個(gè) Int 變量的值。

var someInt = 3 var anotherInt = 107 swapTwoInts(&someInt, &anotherInt) print("someInt is now \(someInt), and anotherInt is now \(anotherInt)") // Prints "someInt is now 107, and anotherInt is now 3"

swapTwoInts(_:_:) 函數(shù)很實(shí)用,但是它只能用于 Int 值。如果你想交換兩個(gè) String 值,或者兩個(gè) Double  值,你只能再寫(xiě)更多的函數(shù),比如下面的 swapTwoStrings(_:_:) 和swapTwoDoubles(_:_:) 函數(shù):

  1. func swapTwoStrings(_ a: inout String, _ b: inout String) { 

  2.     let temporaryA = a 

  3.     a = b 

  4.     b = temporaryA 


func swapTwoDoubles(_ a: inout Double, _ b: inout Double) {     let temporaryA = a     a = b     b = temporaryA }

你可能已經(jīng)注意到了, swapTwoInts(_:_:) 、 swapTwoStrings(_:_:) 、 swapTwoDoubles(_:_:)  函數(shù)體是一樣的。唯一的區(qū)別是它們接收值類型不同( Int 、 String 和 Double )。

寫(xiě)一個(gè)可以交換任意類型值的函數(shù)會(huì)更實(shí)用、更靈活。泛型代碼讓你能寫(xiě)出這樣的函數(shù)。(下文中定義了這些函數(shù)的泛型版本。)

三個(gè)函數(shù)中, a 和 b 被定義為了相同的類型,這一點(diǎn)很重要。如果 a 和 b 類型不一樣,不能交換它們的值。Swift  是類型安全的語(yǔ)言,不允許(例如)一個(gè) String 類型的變量和一個(gè)Double 類型的變量交換值。嘗試這樣做會(huì)引發(fā)一個(gè)編譯錯(cuò)誤。

3. 泛型函數(shù)

泛型函數(shù)可以用于任何類型。這里是上面提到的 swapTwoInts(_:_:) 函數(shù)的泛型版本,叫做swapTwoValues(_:_:) :

func swapTwoValues<T>(_ a: inout T, _ b: inout T) {     let temporaryA = a     a = b     b = temporaryA }

swapTwoValues(_:_:) 和 swapTwoInts(_:_:) 函數(shù)體是一樣的。但是, swapTwoValues(_:_:)  和swapTwoInts(_:_:) 的第一行有點(diǎn)不一樣。下面是首行的對(duì)比:

func swapTwoInts(_ a: inout Int, _ b: inout Int) func swapTwoValues<T>(_ a: inout T, _ b: inout T)

泛型版本的函數(shù)用了一個(gè)占位符類型名(這里叫做 T ),而不是一個(gè)實(shí)際的類型名(比如 Int 、String 或 Double )。占位符類型名沒(méi)有聲明 T  必須是什么樣的,但是它確實(shí)說(shuō)了 a 和 b 必須都是同一個(gè)類型 T ,或者說(shuō)都是 T 所表示的類型。替代 T  實(shí)際使用的類型將在每次調(diào)用swapTwoValues(_:_:) 函數(shù)時(shí)決定。

其他的區(qū)別是泛型函數(shù)名( swapTwoValues(_:_:) )后面有包在尖括號(hào)()里的占位符類型名( T )。尖括號(hào)告訴 Swift, T  是一個(gè) swapTwoValues(_:_:) 函數(shù)定義里的占位符類型名。因?yàn)?T 是一個(gè)占位符,Swift 不會(huì)查找真的叫 T 的類型。

現(xiàn)在,可以用調(diào)用 swapTwoInts 的方式來(lái)調(diào)用 swapTwoValues(_:_:)  函數(shù),除此之外,可以給函數(shù)傳遞兩個(gè)任意類型的值,只要兩個(gè)實(shí)參的類型一致即可。每次調(diào)用 swapTwoValues(_:_:) ,用于T  的類型會(huì)根據(jù)傳入函數(shù)的值類型自動(dòng)推斷。

在下面的兩個(gè)例子中, T分別被推斷為 Int 和 String :

var someInt = 3 var anotherInt = 107 swapTwoValues(&someInt, &anotherInt) // someInt is now 107, and anotherInt is now 3   var someString = "hello" var anotherString = "world" swapTwoValues(&someString, &anotherString) // someString is now "world", and anotherString is now "hello"

上面定義的 swapTwoValues(_:_:) 函數(shù)受一個(gè)名為 swap 的泛型函數(shù)啟發(fā), swap 函數(shù)是 Swift  標(biāo)準(zhǔn)庫(kù)的一部分,可以用于你的應(yīng)用中。如果你需要在你自己的代碼中用swapTwoValues(_:_:) 函數(shù)的功能,可以直接用 Swift 提供的  swap(_:_:) 函數(shù),不需要自己實(shí)現(xiàn)。

4. 類型形式參數(shù)

上面的 swapTwoValues(_:_:) 中,占位符類型 T  就是一個(gè)類型形式參數(shù)的例子。類型形式參數(shù)指定并且命名一個(gè)占位符類型,緊挨著寫(xiě)在函數(shù)名后面的一對(duì)尖括號(hào)里(比如)。

一旦你指定了一個(gè)類型形式參數(shù),你就可以用它定義一個(gè)函數(shù)形式參數(shù)(比如swapTwoValues(_:_:) 函數(shù)中的形式參數(shù) a 和 b  )的類型,或者用它做函數(shù)返回值類型,或者做函數(shù)體中類型標(biāo)注。在不同情況下,用調(diào)用函數(shù)時(shí)的實(shí)際類型來(lái)替換類型形式參數(shù)。(上面的swapTwoValues(_:_:)  例子中,第一次調(diào)用函數(shù)的時(shí)候用 Int 替換了 T ,第二次調(diào)用是用 String替換的。)

你可以通過(guò)在尖括號(hào)里寫(xiě)多個(gè)用逗號(hào)隔開(kāi)的類型形式參數(shù)名,來(lái)提供更多類型形式參數(shù)。

5. 命名類型形式參數(shù)

大多數(shù)情況下,類型形式參數(shù)的名字要有描述性,比如 Dictionary

類型形式參數(shù)永遠(yuǎn)用大寫(xiě)開(kāi)頭的駝峰命名法(比如T和MyTypeParameter)命名,以指明它們是一個(gè)類型的占位符,不是一個(gè)值。

6. 泛型類型

除了泛型函數(shù),Swift允許你定義自己的泛型類型。它們是可以用于任意類型的自定義類、結(jié)構(gòu)體、枚舉,和 Array 、 Dictionary  方式類似。

本章將向你展示如何寫(xiě)出一個(gè)叫做 Stack 的泛型集合類型。棧是值的有序集合,和數(shù)組類似,但是比 Swift 的 Array  類型有更嚴(yán)格的操作限制。數(shù)組允許在其中任何位置插入和移除元素。但是,棧的新元素只能添加到集合的末尾(這就是所謂的壓棧)。同樣,棧只允許從集合的末尾移除元素(這就是所謂的出棧)。

UINavigationController類在它的導(dǎo)航層級(jí)關(guān)系中管理視圖控制器就是用的棧的思想。你可以調(diào)用UINavigationController類的pushViewController(_:animated:)  方法添加(或者說(shuō)push)一個(gè)視圖控制器到導(dǎo)航棧里,用popViewControllerAnimated(_:)  方法從導(dǎo)航棧移除(或者說(shuō)pop)一個(gè)視圖控制器。當(dāng)你需要用嚴(yán)格的”后進(jìn),先出”方式管理一個(gè)集合時(shí),棧是一個(gè)很有用的集合模型。

下面的圖示展示了壓棧和出棧的行為:

Swift的泛型特性是怎樣的
  1. 鴻蒙官方戰(zhàn)略合作共建——HarmonyOS技術(shù)社區(qū)

  2. 現(xiàn)在棧里有三個(gè)值;

  3. 第四個(gè)值壓到棧頂;

  4. 棧里現(xiàn)在有四個(gè)值,最近添加的那個(gè)在頂部;

  5. 棧中頂部的元素被移除,或者說(shuō)叫”出棧”;

  6. 移除一個(gè)元素之后,棧里又有三個(gè)元素了。

這里是如何寫(xiě)一個(gè)非泛型版本的棧,這種情況是一個(gè) Int 值的棧:

struct IntStack {     var items = [Int]()     mutating func push(_ item: Int) {         items.append(item)     }     mutating func pop() -> Int {         return items.removeLast()     } }

這個(gè)結(jié)構(gòu)體用了一個(gè)叫做 items 的 Array 屬性去存儲(chǔ)棧中的值。 Stack 提供兩個(gè)方法, push 和pop  ,用于添加和移除棧中的值。這兩個(gè)方法被標(biāo)記為 mutating ,是因?yàn)樗麄冃枰薷?或者說(shuō)改變)結(jié)構(gòu)體的 items 數(shù)組。

上面展示的 IntStack 類型只能用于 Int 值。但是定義一個(gè)泛型 Stack 會(huì)更實(shí)用,這樣可以管理任何類型值的棧。

這里有一個(gè)相同代碼的泛型版本:

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

注意,這個(gè)泛型的 Stack 和非泛型版本的本質(zhì)上是一樣的,只是用一個(gè)叫做 Element 的類型形式參數(shù)代替了實(shí)際的 Int  類型。這個(gè)類型形式參數(shù)寫(xiě)在一對(duì)尖括號(hào)()里,緊跟在結(jié)構(gòu)體名字后面。

Element 為稍后提供的”某類型 Element “定義了一個(gè)占位符名稱。這個(gè)未來(lái)的類型可以在結(jié)構(gòu)體定義內(nèi)部任何位置以” Element  “引用。在這個(gè)例子中,有三個(gè)地方將 Element 作為一個(gè)占位符使用:

  • 創(chuàng)建一個(gè)名為 items 的屬性,用一個(gè) Element 類型值的空數(shù)組初始化這個(gè)屬性;

  • 指定 push(_:) 方法有一個(gè)叫做 item 的形式參數(shù),其必須是 Element 類型;

  • 指定 pop() 方法的返回值是一個(gè) Element 類型的值。

因?yàn)樗欠盒停虼四芤?Array 和 Dictionary 相似的方式,用 Stack 創(chuàng)建一個(gè)Swift 中有效的任意類型的棧。

通過(guò)在尖括號(hào)中寫(xiě)出存儲(chǔ)在棧里的類型,來(lái)創(chuàng)建一個(gè)新的 Stack 實(shí)例。例如,創(chuàng)建一個(gè)新的字符串棧,可以寫(xiě) Stack() :

var stackOfStrings = Stack<String>() stackOfStrings.push("uno") stackOfStrings.push("dos") stackOfStrings.push("tres") stackOfStrings.push("cuatro") // the stack now contains 4 strings

這是往棧里壓入四個(gè)值之后, stackOfStrings 的圖示:

Swift的泛型特性是怎樣的

從棧中移除并返回頂部的值,"cuatro" :

let fromTheTop = stackOfStrings.pop() // fromTheTop is equal to "cuatro", and the stack now contains 3 strings

這是棧頂部的值出棧后的棧圖示:

Swift的泛型特性是怎樣的

7. 擴(kuò)展一個(gè)泛型類型

當(dāng)你擴(kuò)展一個(gè)泛型類型時(shí),不需要在擴(kuò)展的定義中提供類型形式參數(shù)列表。原始類型定義的類型形式參數(shù)列表在擴(kuò)展體里仍然有效,并且原始類型形式參數(shù)列表名稱也用于擴(kuò)展類型形式參數(shù)。

下面的例子擴(kuò)展了泛型 Stack 類型,向其中添加一個(gè)叫做 topItem 的只讀計(jì)算屬性,不需要從棧里移除就能返回頂部的元素:

extension Stack {     var topItem: Element? {         return items.isEmpty ? nil : items[items.count - 1]     } }

topItem 屬性返回一個(gè) Element 類型的可選值。如果棧是空的, topItem 返回 nil ;如果棧非空, topItem 返回 items  數(shù)組的最后一個(gè)元素。

注意,這個(gè)擴(kuò)展沒(méi)有定義類型形式參數(shù)列表。相反,擴(kuò)展中用 Stack已有的類型形式參數(shù)名稱,Element ,來(lái)指明計(jì)算屬性 topItem  的可選項(xiàng)類型。

現(xiàn)在,不用移除元素,就可以用任何 Stack 實(shí)例的 topItem 計(jì)算屬性來(lái)訪問(wèn)和查詢它頂部的元素:

if let topItem = stackOfStrings.topItem {     print("The top item on the stack is \(topItem).") } // Prints "The top item on the stack is tres."

8. 類型約束

swapTwoValues(_:_:) 函數(shù)和 Stack  類型可以用于任意類型。但是,有時(shí)在用于泛型函數(shù)的類型和泛型類型上,強(qiáng)制其遵循特定的類型約束很有用。類型約束指出一個(gè)類型形式參數(shù)必須繼承自特定類,或者遵循一個(gè)特定的協(xié)議、組合協(xié)議。

例如,Swift 的 Dictionary  類型在可以用于字典中鍵的類型上設(shè)置了一個(gè)限制。如字典中描述的一樣,字典鍵的類型必須是是可哈希的。也就是說(shuō),它必須提供一種使其可以唯一表示的方法。Dictionary  需要它的鍵是可哈希的,以便它可以檢查字典中是否包含一個(gè)特定鍵的值。沒(méi)有了這個(gè)要求, Dictionary  不能區(qū)分該插入還是替換一個(gè)指定鍵的值,也不能在字典中查找已經(jīng)給定的鍵的值。

這個(gè)要求通過(guò) Dictionary 鍵類型上的類型約束實(shí)現(xiàn),它指明了鍵類型必須遵循 Swift 標(biāo)準(zhǔn)庫(kù)中定義的 Hashable 協(xié)議。所有 Swift  基本類型(比如 String 、 Int 、 Double 和 Bool )默認(rèn)都是可哈希的。

創(chuàng)建自定義泛型類型時(shí),你可以定義你自己的類型約束,這些約束可以提供強(qiáng)大的泛型編程能力。像 Hashable  這樣的抽象概念,根據(jù)概念上的特征,而不是確切的類型來(lái)表征類型。

? 8.1 類型約束語(yǔ)法

在一個(gè)類型形式參數(shù)名稱后面放置一個(gè)類或者協(xié)議作為形式參數(shù)列表的一部分,并用冒號(hào)隔開(kāi),以寫(xiě)出一個(gè)類型約束。下面展示了一個(gè)泛型函數(shù)類型約束的基本語(yǔ)法(和泛型類型的語(yǔ)法相同):

func someFunction<T: SomeClass, U: SomeProtocol>(someT: T, someU: U) {     // function body goes here }

上面的假想函數(shù)有兩個(gè)形式參數(shù)。第一個(gè)類型形式參數(shù), T ,有一個(gè)類型約束要求 T 是SomeClass 的子類。第二個(gè)類型形式參數(shù), U  ,有一個(gè)類型約束要求 U 遵循 SomeProtocol 協(xié)議。

? 8.2 類型約束的應(yīng)用

這是一個(gè)叫做 findIndex(ofString:in:) 的非泛型函數(shù),在給定的 String 值數(shù)組中查找給定的String 值。  findIndex(ofString:in:) 函數(shù)返回一個(gè)可選的 Int  值,如果找到了給定字符串,它會(huì)返回?cái)?shù)組中第一個(gè)匹配的字符串的索引值,如果找不到給定字符串就返回 nil:

func findIndex(ofString valueToFind: String, in array: [String]) -> Int? {     for (index, value) in array.enumerated() {         if value == valueToFind {             return index         }     }     return nil }

findIndex(ofString:in:) 函數(shù)可以用于字符串?dāng)?shù)組中查找字符串值:

let strings = ["cat", "dog", "llama", "parakeet", "terrapin"] if let foundIndex = findIndex(ofString: "llama", in: strings) {     print("The index of llama is \(foundIndex)") } // Prints "The index of llama is 2"

在數(shù)組中查找值的索引的原理只能用于字符串。但是,通過(guò)某種 T 類型的值代替所有用到的字符串,你可以用泛型函數(shù)寫(xiě)一個(gè)相同的功能。

這里寫(xiě)出了一個(gè)叫做 findIndex(of:in:) 的函數(shù),可能是你期望的 findIndex(ofString:in:)  函數(shù)的一個(gè)泛型版本。注意,函數(shù)的返回值仍然是 Int?  ,因?yàn)楹瘮?shù)返回一個(gè)可選的索引數(shù)字,而不是數(shù)組里的一個(gè)可選的值。這個(gè)函數(shù)沒(méi)有編譯,例子后面會(huì)解釋原因:

func findIndex<T>(of valueToFind: T, in array:[T]) -> Int? {     for (index, value) in array.enumerated() {         if value == valueToFind {             return index         }     }     return nil }

這個(gè)函數(shù)沒(méi)有像上面寫(xiě)的那樣編譯。問(wèn)題在于相等檢查,”if value == valueToFind “。Swift 中的類型不是每種都能用相等操作符(  == )來(lái)比較的。如果你創(chuàng)建自己的類或者結(jié)構(gòu)體去描述一個(gè)復(fù)雜的數(shù)據(jù)模型,比如說(shuō),對(duì)于那個(gè)類或結(jié)構(gòu)體來(lái)說(shuō),”相等”的意義不是 Swift  能替你猜出來(lái)的。因此,不能保證這份代碼可以用于所有 T 可以表示的類型,當(dāng)你嘗試編譯這份代碼時(shí)會(huì)提示一個(gè)相應(yīng)的錯(cuò)誤。

并非無(wú)路可走,總之,Swift 標(biāo)準(zhǔn)庫(kù)中定義了一個(gè)叫做 Equatable 的協(xié)議,要求遵循其協(xié)議的類型要實(shí)現(xiàn)相等操作符( == )和不等操作符( !=  ),用于比較該類型的任意兩個(gè)值。所有 Swift 標(biāo)準(zhǔn)庫(kù)中的類型自動(dòng)支持 Equatable 協(xié)議。

任何 Equatable 的類型都能安全地用于 findIndex(of:in:)  函數(shù),因?yàn)榭梢员WC那些類型支持相等操作符。為了表達(dá)這個(gè)事實(shí),當(dāng)你定義函數(shù)時(shí)將 Equatable 類型約束作為類型形式參數(shù)定義的一部分書(shū)寫(xiě):

func findIndex<T: Equatable>(of valueToFind: T, in array:[T]) -> Int? {     for (index, value) in array.enumerated() {         if value == valueToFind {             return index         }     }     return nil }

findIndex(of:in:) 的類型形式參數(shù)寫(xiě)作 T: Equatable ,表示”任何遵循 Equatable 協(xié)議的類型 T“。

findIndex(of:in:) 函數(shù)現(xiàn)在可以成功編譯,并且可以用于任何 Equatable 的類型,比如 Double或者 String :

let doubleIndex = findIndex(of: 9.3, in: [3.14159, 0.1, 0.25]) // doubleIndex is an optional Int with no value, because 9.3 is not in the array let stringIndex = findIndex(of: "Andrea", in: ["Mike", "Malcolm", "Andrea"]) // stringIndex is an optional Int containing a value of 2

9. 關(guān)聯(lián)類型

定義一個(gè)協(xié)議時(shí),有時(shí)在協(xié)議定義里聲明一個(gè)或多個(gè)關(guān)聯(lián)類型是很有用的。關(guān)聯(lián)類型給協(xié)議中用到的類型一個(gè)占位符名稱。直到采納協(xié)議時(shí),才指定用于該關(guān)聯(lián)類型的實(shí)際類型。關(guān)聯(lián)類型通過(guò)associatedtype  關(guān)鍵字指定。

? 9.1 關(guān)聯(lián)類型的應(yīng)用

這里是一個(gè)叫做 Container 的示例協(xié)議,聲明了一個(gè)叫做 ItemType 的關(guān)聯(lián)類型:

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

Container 協(xié)議定義了三個(gè)所有容器必須提供的功能:

  • 必須能夠通過(guò) append(_:) 方法向容器中添加新元素;

  • 必須能夠通過(guò)一個(gè)返回 Int 值的 count 屬性獲取容器中的元素?cái)?shù)量;

  • 必須能夠通過(guò) Int 索引值的下標(biāo)取出容器中每個(gè)元素。

這個(gè)協(xié)議沒(méi)有指定元素如何儲(chǔ)存在容器中,也沒(méi)指定允許存入容器的元素類型。協(xié)議僅僅指定了想成為一個(gè) Container  的類型,必須提供的三種功能。遵循該協(xié)議的類型可以提供其他功能,只要滿足這三個(gè)要求即可。

任何遵循 Container  協(xié)議的類型必須能指定其存儲(chǔ)值的類型。尤其是它必須保證只有正確類型的元素才能添加到容器中,而且該類型下標(biāo)返回的元素類型必須是正確的。

為了定義這些要求, Container 協(xié)議需要一種在不知道容器具體類型的情況下,引用該容器將存儲(chǔ)的元素類型的方法。 Container  協(xié)議需要指定所有傳給 append(_:) 方法的值必須和容器里元素的值類型是一樣的,而且容器下標(biāo)返回的值也是和容器里元素的值類型相同。

為了實(shí)現(xiàn)這些要求, Container 協(xié)議聲明了一個(gè)叫做 ItemType 的關(guān)聯(lián)類型,寫(xiě)作 associatedtype ItemType  。協(xié)議沒(méi)有定義 ItemType 是什么類型,這個(gè)信息留給遵循協(xié)議的類型去提供。但是,ItemType 這個(gè)別名,提供了一種引用 Container  中元素類型的方式,定義了一種用于 Container方法和下標(biāo)的類型,確保了任何 Container 期待的行為都得到滿足。

這是前面非泛型版本的 IntStack ,使其遵循 Container 協(xié)議:

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

IntStack 實(shí)現(xiàn)了 Container 協(xié)議所有的要求,為滿足這些要求,封裝了 IntStack 里現(xiàn)有的方法。

此外, IntStack 為了實(shí)現(xiàn) Container 協(xié)議,指定了適用于 ItemType 的類型是 Int 類型。typealias ItemType  = Int 把 ItemType 抽象類型轉(zhuǎn)換為了具體的 Int 類型。

感謝 Swift 的類型推斷功能,你不用真的在 IntStack 定義中聲明一個(gè)具體的 Int 類型 ItemType。因?yàn)?IntStack 遵循  Container 協(xié)議的所有要求,通過(guò)簡(jiǎn)單查看 append(_:) 方法的 item 形式參數(shù)和下標(biāo)的返回類型,Swift 可以推斷出合適的  ItemType 。如果你真的從上面的代碼中刪除typealias ItemType = Int ,一切都會(huì)正常運(yùn)行,因?yàn)?ItemType  該用什么類型是非常明確的。

你也可以做一個(gè)遵循 Container 協(xié)議的泛型 Stack 類型:

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

這次,類型形式參數(shù) Element 用于 append(_:) 方法的 item 形式參數(shù)和下標(biāo)的返回類型。因此,對(duì)于這個(gè)容器,Swift 可以推斷出  Element 是適用于 ItemType 的類型。

? 9.2 給關(guān)聯(lián)類型添加約束

你可以在協(xié)議里給關(guān)聯(lián)類型添加約束來(lái)要求遵循的類型滿足約束。比如說(shuō),下面的代碼定義了一個(gè)版本的 Container  ,它要求容器中的元素都是可判等的。

protocol Container {     associatedtype Item: Equatable     mutating func append(_ item: Item)     var count: Int { get }     subscript(i: Int) -> Item { get } }

要遵循這個(gè)版本的 Container ,容器的 Item 必須遵循 Equatable 協(xié)議。

? 9.3 在關(guān)聯(lián)類型約束里使用協(xié)議

協(xié)議可以作為它自身的要求出現(xiàn)。比如說(shuō),這里有一個(gè)協(xié)議細(xì)化了 Container 協(xié)議,添加了一個(gè) suffix(_:) 方法。 suffix(_:)  方法返回容器中從后往前給定數(shù)量的元素,把它們存儲(chǔ)在一個(gè) Suffix 類型的實(shí)例里。

protocol SuffixableContainer: Container {     associatedtype Suffix: SuffixableContainer where Suffix.Item == Item     func suffix(_ size: Int) -> Suffix }

在這個(gè)協(xié)議里, Suffix 是一個(gè)關(guān)聯(lián)類型,就像上邊例子中 Container 的 Item 類型一樣。Suffix 擁有兩個(gè)約束:它必須遵循  SuffixableContainer 協(xié)議(就是當(dāng)前定義的協(xié)議),以及它的 Item 類型必須是和容器里的 Item 類型相同。Item 的約束是一個(gè)  where 分句,它在下面帶有泛型 Where 分句的擴(kuò)展中有討論。

這里有一個(gè)來(lái)自閉包的循環(huán)強(qiáng)引用的 Stack 類型的擴(kuò)展,它添加了對(duì) SuffixableContainer協(xié)議的遵循:

extension Stack: SuffixableContainer {     func suffix(_ size: Int) -> Stack {         var result = Stack()         for index in (count-size)..<count {             result.append(self[index])         }         return result     }     // Inferred that Suffix is Stack. } var stackOfInts = Stack<Int>() stackOfInts.append(10) stackOfInts.append(20) stackOfInts.append(30) let suffix = stackOfInts.suffix(2) // suffix contains 20 and 30

在上面的例子中, Suffix 是 Stack 的關(guān)聯(lián)類型,也就是 Stack ,所以 Stack 的后綴運(yùn)算返回另一個(gè) Stack 。另外,遵循  SuffixableContainer 的類型可以擁有一個(gè)與它自己不同的Suffix  類型&mdash;&mdash;也就是說(shuō)后綴運(yùn)算可以返回不同的類型。比如說(shuō),這里有一個(gè)非泛型IntStack 類型的擴(kuò)展,它添加了 SuffixableContainer 遵循,使用  Stack作為它的后綴類型而不是 IntStack :

extension IntStack: SuffixableContainer {     func suffix(_ size: Int) -> Stack<Int> {         var result = Stack<Int>()         for index in (count-size)..<count {             result.append(self[index])         }         return result     }     // Inferred that Suffix is Stack<Int>. }

? 9.4 擴(kuò)展現(xiàn)有類型來(lái)指定關(guān)聯(lián)類型

你可以擴(kuò)展一個(gè)現(xiàn)有類型使其遵循一個(gè)協(xié)議,如在擴(kuò)展里添加協(xié)議遵循描述的一樣。這包括一個(gè)帶關(guān)聯(lián)類型的協(xié)議。

Swift 的 Array 類型已經(jīng)提供了 append(_:) 方法、 count 屬性、用 Int 索引取出其元素的下標(biāo)。這三個(gè)功能滿足了  Container 協(xié)議的要求。這意味著你可以通過(guò)簡(jiǎn)單地聲明 Array 采納協(xié)議,擴(kuò)展 Array 使其遵循 Container  協(xié)議。通過(guò)一個(gè)空的擴(kuò)展實(shí)現(xiàn),如使用擴(kuò)展聲明采納協(xié)議:

extension Array: Container {}

數(shù)組已有的 append(_:) 方法和下標(biāo)使得 Swift 能為 ItemType 推斷出合適的類型,就像上面的泛型 Stack  類型一樣。定義這個(gè)擴(kuò)展之后,你可以把任何 Array 當(dāng)做一個(gè) Container 使用。

10. 泛型Where分句

如類型約束中描述的一樣,類型約束允許你在泛型函數(shù)或泛型類型相關(guān)的類型形式參數(shù)上定義要求。

類型約束在為關(guān)聯(lián)類型定義要求時(shí)也很有用。通過(guò)定義一個(gè)泛型Where分句來(lái)實(shí)現(xiàn)。泛型 Where  分句讓你能夠要求一個(gè)關(guān)聯(lián)類型必須遵循指定的協(xié)議,或者指定的類型形式參數(shù)和關(guān)聯(lián)類型必須相同。泛型 Where 分句以 Where  關(guān)鍵字開(kāi)頭,后接關(guān)聯(lián)類型的約束或類型和關(guān)聯(lián)類型一致的關(guān)系。泛型 Where 分句寫(xiě)在一個(gè)類型或函數(shù)體的左半個(gè)大括號(hào)前面。

下面的例子定義了一個(gè)叫做 allItemsMatch 的泛型函數(shù),用來(lái)檢查兩個(gè) Container  實(shí)例是否包含相同順序的相同元素。如果所有元素都匹配,函數(shù)返回布爾值 ture ,否則返回 false 。

被檢查的兩個(gè)容器不一定是相同類型的(盡管它們可以是),但是它們的元素類型必須相同。這個(gè)要求通過(guò)類型約束和泛型 Where 分句一起體現(xiàn):

func allItemsMatch<C1: Container, C2: Container>     (_ someContainer: C1, _ anotherContainer: C2) -> Bool     where C1.ItemType == C2.ItemType, C1.ItemType: Equatable {                  // Check that both containers contain the same number of items.         if someContainer.count != anotherContainer.count {             return false         }                  // Check each pair of items to see if they are equivalent.         for i in 0..<someContainer.count {             if someContainer[i] != anotherContainer[i] {                 return false             }         }                  // All items match, so return true.         return true }

這個(gè)函數(shù)有兩個(gè)形式參數(shù),someContainer 和 anotherContainer 。 someContainer 形式參數(shù)是 C1類型,  anotherContainer 形式參數(shù)是 C2 類型。 C1 和 C2 是兩個(gè)容器類型的類型形式參數(shù),它們的類型在調(diào)用函數(shù)時(shí)決定。

下面是函數(shù)的兩個(gè)類型形式參數(shù)上設(shè)置的要求:

  • C1 必須遵循 Container 協(xié)議(寫(xiě)作 C1: Container );

  • C2 也必須遵循 Container 協(xié)議(寫(xiě)作 C2: Container );

  • C1 的 ItemType 必須和 C2 的 ItemType 相同(寫(xiě)作 C1.ItemType == C2.ItemType );

  • C1 的 ItemType 必須遵循 Equatable 協(xié)議(寫(xiě)作 C1.ItemType: Equatable )。

前兩個(gè)要求定義在了函數(shù)的類型形式參數(shù)列表里,后兩個(gè)要求定義在了函數(shù)的泛型 Where 分句中。

這些要求意味著:

  • someContainer 是一個(gè) C1 類型的容器;

  • anotherContainer 是一個(gè) C2 類型的容器;

  • someContainer 和 anotherContainer 中的元素類型相同;

  • someContainer 中的元素可以通過(guò)不等操作符( != )檢查它們是否不一樣。

后兩個(gè)要求放到一起意味著, anotherContainer 中的元素也可以通過(guò) != 操作符檢查,因?yàn)樗鼈兒?someContainer  中的元素類型完全相同。

這些要求使得 allItemsMatch(_:_:) 函數(shù)可以比較兩個(gè)容器,即使它們是不同類型的容器。

allItemsMatch(_:_:) 函數(shù)開(kāi)始會(huì)先檢查兩個(gè)容器中的元素?cái)?shù)量是否相同。如果它們的元素?cái)?shù)量不同,它們不可能匹配,函數(shù)就會(huì)返回 false  。

檢查完數(shù)量之后,用一個(gè) for-in 循環(huán)和半開(kāi)區(qū)間操作符( ..< )遍歷 someContainer 中的所有元素。函數(shù)會(huì)檢查  someContainer 中的每個(gè)元素,是否和 anotherContainer 中對(duì)應(yīng)的元素不相等。如果兩個(gè)元素不相等,則兩個(gè)容器不匹配,函數(shù)返回  false 。

如果循環(huán)完成都沒(méi)有出現(xiàn)不匹配的情況,兩個(gè)容器就是匹配的,則函數(shù)返回 true 。

這是 allItemsMatch(_:_:) 函數(shù)使用的示例:

var stackOfStrings = Stack<String>() stackOfStrings.push("uno") stackOfStrings.push("dos") stackOfStrings.push("tres")   var arrayOfStrings = ["uno", "dos", "tres"]   if allItemsMatch(stackOfStrings, arrayOfStrings) {     print("All items match.") } else {     print("Not all items match.") } // Prints "All items match."

上面的例子創(chuàng)建了一個(gè) Stack 實(shí)例來(lái)存儲(chǔ) String 值,壓到棧中三個(gè)字符串。還創(chuàng)建了一個(gè)  Array實(shí)例,用三個(gè)同樣字符串的字面量初始化該數(shù)組。雖然棧和數(shù)組的類型不一樣,但它們都遵循Container 協(xié)議,并且它們包含的值類型一樣。因此,你可以調(diào)用  allItemsMatch(_:_:) 函數(shù),用那兩個(gè)容器做函數(shù)的形式參數(shù)。上面的例子中, allItemsMatch(_:_:)  函數(shù)正確地報(bào)告了兩個(gè)容器中所有元素匹配。

11. 帶有泛型 Where 分句的擴(kuò)展

你同時(shí)也可以使用泛型的 where 分句來(lái)作為擴(kuò)展的一部分。下面的泛型 Stack 結(jié)構(gòu)體的擴(kuò)展了先前的栗子,添加了一個(gè) isTop(_:)  方法。

extension Stack where Element: Equatable {     func isTop(_ item: Element) -> Bool {         guard let topItem = items.last else {             return false         }         return topItem == item     } }

這個(gè)新的 isTop(_:) 方法首先校驗(yàn)棧不為空,然后對(duì)比給定的元素與棧頂元素。如果你嘗試不使用泛型 where 分句來(lái)做這個(gè),你可能會(huì)遇到一個(gè)問(wèn)題:  isTop(_:) 的實(shí)現(xiàn)要使用 == 運(yùn)算符,但Stack 的定義并不需要其元素可相等,所以使用 == 運(yùn)算符會(huì)導(dǎo)致運(yùn)行時(shí)錯(cuò)誤。使用泛型  where分句則允許你給擴(kuò)展添加一個(gè)新的要求,這樣擴(kuò)展只會(huì)在棧內(nèi)元素可判等的時(shí)候才給棧添加isTop(_:) 方法。

這是用法:

if stackOfStrings.isTop("tres") {     print("Top element is tres.") } else {     print("Top element is something else.") } // Prints "Top element is tres."

如果嘗試在元素不能判等的棧調(diào)用 isTop(_:) 方法,你就會(huì)出發(fā)運(yùn)行時(shí)錯(cuò)誤。

struct NotEquatable { } var notEquatableStack = Stack<NotEquatable>() let notEquatableValue = NotEquatable() notEquatableStack.push(notEquatableValue) notEquatableStack.isTop(notEquatableValue) // Error

你可以使用泛型 where 分句來(lái)擴(kuò)展到一個(gè)協(xié)議。下面的栗子把先前的 Container 協(xié)議擴(kuò)展添加了一個(gè) startsWith(_:) 方法。

extension Container where Item: Equatable {     func startsWith(_ item: Item) -> Bool {         return count >= 1 && self[0] == item     } }

startsWith(_:) 方法首先確保容器擁有至少一個(gè)元素,然后它檢查第一個(gè)元素是否與給定元素相同。這個(gè)新的 startsWith(_:)  方法可以應(yīng)用到任何遵循 Container 協(xié)議的類型上,包括前面我們用的棧和數(shù)組,只要容器的元素可以判等。

if [9, 9, 9].startsWith(42) {     print("Starts with 42.") } else {     print("Starts with something else.") } // Prints "Starts with something else."

上邊例子中的泛型 where 分句要求 Item 遵循協(xié)議,但你同樣可以寫(xiě)一個(gè)泛型 where 分句來(lái)要求Item 為特定類型。比如:

extension Container where Item == Double {     func average() -> Double {         var sum = 0.0         for index in 0..<count {             sum += self[index]         }         return sum / Double(count)     } } print([1260.0, 1200.0, 98.6, 37.0].average()) // Prints "648.9"

這個(gè)栗子在 Item 是 Double 時(shí)給容器添加了 average()  方法。它遍歷容器中的元素來(lái)把它們相加,然后除以容器的總數(shù)來(lái)計(jì)算平均值。它顯式地把總數(shù)從 Int 轉(zhuǎn)為 Double 來(lái)允許浮點(diǎn)除法。

你可以在一個(gè)泛型 where 分句中包含多個(gè)要求來(lái)作為擴(kuò)展的一部分,就如同你在其它地方寫(xiě)的泛型 where 分句一樣。每一個(gè)需求用逗號(hào)分隔。

12. 上下文 Where 分句

當(dāng)你已經(jīng)在范型類型上下文中時(shí),你可以把范型 where  分句作為聲明的一部分,它自己沒(méi)有范型類型約束。比如說(shuō),你可以在范型類型的下標(biāo)腳本或者范型類型擴(kuò)展的方法中寫(xiě)范型 where 分句。 Container  結(jié)構(gòu)體是范型,下邊例子中的 where 分句寫(xiě)明了容器中新方法需要滿足什么要求才能可用。

extension Container {     func average() -> Double where Item == Int {         var sum = 0.0         for index in 0..<count {             sum += Double(self[index])         }         return sum / Double(count)     }     func endsWith(_ item: Item) -> Bool where Item: Equatable {         return count >= 1 && self[count-1] == item     } } let numbers = [1260, 1200, 98, 37] print(numbers.average()) // Prints "648.75" print(numbers.endsWith(37)) // Prints "true"

這個(gè)例子在元素是整數(shù)時(shí)給 Container 添加了一個(gè) average() 方法,它還在元素是可判等的情況下添加了 endsWith(_:)  方法。這兩個(gè)函數(shù)都包含了范型 where 分句,它給范型原本聲明在Container 中的形式參數(shù) Item 類型添加了類型限制。

如果你不想使用上下文 where 分句,你就需要寫(xiě)兩個(gè)擴(kuò)展,每一個(gè)都使用范型 where 分句。下面的例子和上面的例子有著相同的效果。

extension Container where Item == Int {     func average() -> Double {         var sum = 0.0         for index in 0..<count {             sum += Double(self[index])         }         return sum / Double(count)     } } extension Container where Item: Equatable {     func endsWith(_ item: Item) -> Bool {         return count >= 1 && self[count-1] == item     } }

使用了上下文 where 分句, average() 和 endsWith(_:) 都卸載了同一個(gè)擴(kuò)展當(dāng)中,因?yàn)槊恳粋€(gè)方法的范型 where  分句聲明了其生效所需要滿足的前提。把這些需求移動(dòng)到擴(kuò)展的范型 where 分句,可讓方法以相同的情況生效,但這就要求一個(gè)擴(kuò)展對(duì)應(yīng)一種需求了。

13. 關(guān)聯(lián)類型的泛型 Where 分句

你可以在關(guān)聯(lián)類型中包含一個(gè)泛型 where 分句。比如說(shuō),假定你想要做一個(gè)包含遍歷器的Container ,比如標(biāo)準(zhǔn)庫(kù)中 Sequence  協(xié)議那樣。那么你會(huì)這么寫(xiě):

protocol Container {     associatedtype Item     mutating func append(_ item: Item)     var count: Int { get }     subscript(i: Int) -> Item { get }          associatedtype Iterator: IteratorProtocol where Iterator.Element == Item     func makeIterator() -> Iterator }

Iterator 中的泛型 where 分句要求遍歷器以相同的類型遍歷容器內(nèi)的所有元素,無(wú)論遍歷器是什么類型。 makeIterator()  函數(shù)提供了容器的遍歷器的訪問(wèn)。

對(duì)于一個(gè)繼承自其他協(xié)議的協(xié)議來(lái)說(shuō),你可以通過(guò)在協(xié)議的聲明中包含泛型 where 分句來(lái)給繼承的協(xié)議中關(guān)聯(lián)類型添加限定。比如說(shuō),下面的代碼聲明了一個(gè)  ComparableContainer 協(xié)議,它要求 Item 遵循 Comparable :

protocol ComparableContainer: Container where Item: Comparable { }

14. 泛型下標(biāo)

下標(biāo)可以是泛型,它們可以包含泛型 where 分句。你可以在 subscript 后用尖括號(hào)來(lái)寫(xiě)類型占位符,你還可以在下標(biāo)代碼塊花括號(hào)前寫(xiě)泛型 where  分句。舉例來(lái)說(shuō):

extension Container {     subscript<Indices: Sequence>(indices: Indices) -> [Item]         where Indices.Iterator.Element == Int {             var result = [Item]()             for index in indices {                 result.append(self[index])             }             return result     } }

這個(gè) Container 協(xié)議的擴(kuò)展添加了一個(gè)接收一系列索引并返回包含給定索引元素的數(shù)組。這個(gè)泛型下標(biāo)有如下限定:

  • 在尖括號(hào)中的泛型形式參數(shù) Indices 必須是遵循標(biāo)準(zhǔn)庫(kù)中 Sequence 協(xié)議的某類型;

  • 下標(biāo)接收單個(gè)形式參數(shù), indices ,它是一個(gè) Indices 類型的實(shí)例;

  • 泛型 where 分句要求序列的遍歷器必須遍歷 Int 類型的元素。這就保證了序列中的索引都是作為容器索引的相同類型。

合在一起,這些限定意味著傳入的 indices 形式參數(shù)是一個(gè)整數(shù)的序列。

看完上述內(nèi)容,你們對(duì)Swift的泛型特性是怎樣的有進(jìn)一步的了解嗎?如果還想了解更多知識(shí)或者相關(guān)內(nèi)容,請(qǐng)關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道,感謝大家的支持。

新聞標(biāo)題:Swift的泛型特性是怎樣的
本文來(lái)源:http://www.chinadenli.net/article28/piddjp.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供網(wǎng)站內(nèi)鏈微信小程序軟件開(kāi)發(fā)外貿(mào)建站小程序開(kāi)發(fā)

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)

微信小程序開(kāi)發(fā)