Swift 5.1 (21) - 泛型

使用泛型能讓我們寫出靈活的,可復(fù)用的函數(shù)和類型精盅,這些函數(shù)和類型會根據(jù)我們定義的要求與任何類型一起使用。使用泛型我們不僅可以避免重復(fù)的代碼而且能以更加清晰抽象的方式表達(dá)代碼意圖缸榄。

泛型是Swift最強大的特征之一渤弛,并且許多Swift的標(biāo)準(zhǔn)庫都是使用泛型的代碼編譯的。

泛型解決的問題

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

上述三個函數(shù)甚带,分別在交換對應(yīng)類型的兩個值她肯。但回歸函數(shù)的本質(zhì),其實這三個函數(shù)的主體是一樣的鹰贵,只不過他們分別接受Int晴氨、DoubleString三種類型作為函數(shù)的入?yún)ⅰ?br> 基于此編寫一個函數(shù),能交換任何類型的兩個值碉输,會更有用籽前,也更靈活。

泛型函數(shù)

//MARK:編寫泛型函數(shù)
func swapTwoValues<T>(_ a : inout T, _  b : inout T) {
    let temp = a
    a = b
    b = temp
}
//調(diào)用
var a = "Qishare"
var b = "Come On"
swapTwoValues(&a, &b)
print(a,b)//!< Come On Qishare

泛型函數(shù)中會使用占位符類型代替實際的類型名稱敷钾,如Int枝哄,StringDouble等。本例中此占位類型的名稱為T阻荒,此占位符類型名稱并未指定T到底是什么挠锥,而是表示無論T代表什么類型,ab都必須具備相同的T類型侨赡。每次調(diào)用swapTwoValues(_:_:)函數(shù)時蓖租,Swift都需要進行類型推斷,確定代替T使用的實際類型羊壹。

泛型函數(shù)與非泛型函數(shù)的區(qū)別在于蓖宦,編寫泛型函數(shù)時,函數(shù)的名稱后需要使用尖括號< >油猫,并在其中指定占位符類型的名稱:<T>稠茂。<>用以告訴Swift函數(shù)T是此函數(shù)定義的占位符類型名稱。因為T是一個占位符類型情妖,所以Swift不會查找T的實際類型主慰。

類型參數(shù)

類型參數(shù) :泛型函數(shù)調(diào)用時嚣州,可以被函數(shù)實際類型代替的參數(shù)。在泛型函數(shù)名稱后尖括號中指定并命名后共螺,意味著指定了類型參數(shù),我們便可以使用此類型參數(shù)來定義函數(shù)的參數(shù)情竹,函數(shù)的返回值類型藐不。當(dāng)然也可以采用<T,Q,...>的形式定義多個類型參數(shù)。

命名類型參數(shù)

在大多數(shù)情況下秦效,類型參數(shù)命名是具有描述性的雏蛮,例如Dictionary <Key,Value>中的Key阱州、Value以及Array <Element>中的Element挑秉,它向我們展示了類型參數(shù)與我們所使用的泛型類型或泛型函數(shù)之間的關(guān)系。但是苔货,當(dāng)它們之間沒有有意義的關(guān)系時犀概,通常會使用單個字母(例如TUV)來命名它們夜惭。

注意:請始終為類型參數(shù)提供駝峰式的大寫名稱(例如TMyTypeParameter)姻灶,以表明它們是類型的占位符,而不是值诈茧。

泛型類型

除了泛型函數(shù)产喉,Swift還允許我們能定義自己的泛型類型,涵蓋類敢会,結(jié)構(gòu)體曾沈,枚舉類型,并可以與任何類型一起使用鸥昏。和字典或數(shù)組相似塞俱。

接下來我們將定義一個棧的結(jié)構(gòu)體類型,命名為Stack,定義Stack類型之前互广,我們需要知道棧結(jié)構(gòu)的特點是:先入后出敛腌,后入先出。
1.定義只能存儲特定類型的棧

struct Stack {
    var items = [Int]()
    mutating func push(_ item:Int){
        items.append(item)
    }
    mutating func pop(_ item:Int) -> Int {
       return items.removeLast()
    }
}
//調(diào)用
var stack_int = Stack()
stack_int.push(7)
stack_int.push(3)
stack_int.push(2)
print(stack_int)//Stack(items: [7, 3, 2])

如若我需要存儲其他類型呢惫皱?
2.定義泛型類型Stack

struct Stack<Element> {
    var items = [Element]()
    mutating func push(_ item:Element){
        items.append(item)
    }
    mutating func pop(_ item:Element) -> Element {
        return items.removeLast()
    }
}
//調(diào)用
var stack_int = Stack<Int>()
stack_int.push(7)
stack_int.push(3)
stack_int.push(2)
print(stack_int)
var stack_string = Stack<String>()
stack_string.push("QISHARE")
print(stack_string)

注意:泛型類型Stack具有一個稱為Element的類型參數(shù)像樊,而不是Int的實際類型。Element是為此泛型類型定義的占位符類型旅敷,在結(jié)構(gòu)體定義中的任何位置都可以使用Element來引用未來調(diào)用時的實際類型生棍。

泛型類型的擴展

當(dāng)擴展一個泛型類型的時候,我們不需要提供類型參數(shù)的列表作為此擴展定義的一部分媳谁。因為涂滴,定義泛型類型時定義好的類型參數(shù)在其擴展中依舊時可用的友酱。

extension Stack {
    var topItem : Element? {
        items.last
    }
}
//調(diào)用
var stack_string = Stack<String>()
stack_string.push("QISHARE")
if let topItem = stack_string.topItem {
   print(topItem)//!< QISHARE
}

類型約束

泛型函數(shù)和泛型類型雖然可以與任何類型一起使用,但是有時我們需要強制限制可以一起使用的類型柔纵,這個時候就需要使用類型約束缔杉。比如:Swift中DictionaryKey便被約束為必須遵守hashable協(xié)議。
類型約束:指定類型參數(shù)必須繼承自特定的類搁料、遵守某個協(xié)議或協(xié)議組合或详。

類型約束的語法

語法:參數(shù)類型定義時,參數(shù)名稱后放置單獨的類或協(xié)議約束郭计,約束與 參數(shù)名稱之間使用冒號:隔開霸琴。
注意:類型的約束條件只能為類或協(xié)議。

func someFunction<T: SomeClass, U: SomeProtocol>(someT: T, someU: U) {
    // `T`約束為繼承自`SomeClass`的類型 `U`約束為遵守`SomeProtocol` 協(xié)議的類型
}

類型約束的使用

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

上述代碼編譯時會出現(xiàn)出錯:Binary operator '==' cannot be applied to two 'T' operands(操作數(shù))昭伸。因為==操作符在Swift中不是所有類型都支持梧乘。比如,我們自定義的類型庐杨,只有只有實現(xiàn)了Swift標(biāo)準(zhǔn)庫定義的Equatable協(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
}

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

當(dāng)我們定義協(xié)議時辑莫,有時聲明一個或多個關(guān)聯(lián)類型作為協(xié)議定義的一部分是很有用的学歧。
關(guān)聯(lián)類型的作用,主要提供某個類型的占位名稱各吨,然后作為協(xié)議的一部分去使用枝笨。關(guān)聯(lián)類型的實際使用類型直到協(xié)議被實現(xiàn)時才會指定。關(guān)聯(lián)類型使用關(guān)鍵字associatedtype指定揭蜒。

類型關(guān)聯(lián)的使用

//定義協(xié)議使用類型關(guān)聯(lián)
protocol Container {
    associatedtype Item
    mutating func append(_ item : Item)
    var count : Int{get}
    subscript(i:Int)->Item{get}
    
}
//定義整型Stack類型
struct IntStack : Container {
    var items = [Int]()
    mutating func push(_ item:Int){
        items.append(item)
    }
    mutating func pop(_ item:Int) -> Int {
        return items.removeLast()
    }
    //實現(xiàn)協(xié)議時横浑,需要明確關(guān)聯(lián)類型的實際類型
    typealias Item = Int //!< ①

    mutating func append(_ item: Item) {//!< ①若不存在,此處可直接 Int
        push(item)
    }
    
    var count: Int {
        items.count
    }
    subscript(i: Int) -> Int {
        items[i]
    }
}

Typealias Item = Int是針對Container協(xié)議的實現(xiàn)屉更,將Item的抽象類型轉(zhuǎn)換為Int的具體類型徙融。基于Swift的類型推斷瑰谜,通過append(_ :)方法便可以推斷出Item的類型以及下標(biāo)返回值的類型欺冀。
采用關(guān)聯(lián)類型作為協(xié)議定義的一部分時,此協(xié)議也可以被泛型類型實現(xiàn)萨脑。

struct Stack<Element> : Container {
    var items = [Element]()
    mutating func push(_ item:Element){
        items.append(item)
    }
    mutating func pop(_ item:Element) -> Element {
        return items.removeLast()
    }
    //實現(xiàn)協(xié)議
    typealias Item = Element
    //自動提示為`Element`
    mutating func append(_ item: Element) {
        push(item)
    }
    var count: Int {
        items.count
    }
    subscript(i: Int) -> Element {
        items[i]
    }
}

擴展現(xiàn)有類型以指定關(guān)聯(lián)類型

上篇協(xié)議中我們知道隐轩,當(dāng)特定類型已經(jīng)實現(xiàn)了協(xié)議的要求,但尚未聲明該類型遵守協(xié)議渤早≈俺担可以通過擴展聲明該類型遵守此協(xié)議。當(dāng)協(xié)議中定義了關(guān)聯(lián)類型同樣也是可以的。

比如:SwiftArray類型已經(jīng)提供了Container協(xié)議中方法悴灵,屬性要求的實現(xiàn)扛芽,完全匹配Container協(xié)議要求。這意味著我們通過Array的擴展聲明Array遵守Container協(xié)議积瞒,并且Array內(nèi)部對于協(xié)議要求的實現(xiàn)可以推斷出協(xié)議關(guān)聯(lián)類型Item的實際類型川尖。

extension Array : Container{}
//擴展現(xiàn)有類型以指定關(guān)聯(lián)類型?是否成功赡鲜。
extension Array : Container{
    func associateTypeOne(_:Item){}
    func associateTypeTwo(_:Element){}
    func associateTypeThree(_ : Self){}//實現(xiàn)協(xié)議時空厌,Self都會與協(xié)議實現(xiàn)類型進行關(guān)聯(lián)
}

值得注意的是:在我們定義這個擴展之后,we can use any Array as a Container ? 實際上此處知識點還需自己探索一番银酬。
若我們有一個具體未使用關(guān)聯(lián)類型的協(xié)議Int_Container

protocol Int_Container {
    mutating func append(_ item : Int)
    var count : Int{get}
    subscript(i:Int)->Int{get}
}
  1. 定義函數(shù),參數(shù)為協(xié)議類型筐钟。
func testProtocolWithAssociateTypeOne(_ parameter : Container) {
/*報錯:Protocol 'Container' can only be used as a generic 
constraint because it has Self or associated type requirements*/
}
func testProtocolNoAssociatetype(_ parameter : Int_Container){
    //編譯成功
}

2.使用isas判斷某個類型是否遵守特定協(xié)議

let array : [Any] = [1,"ddd",3]
if array is Container {
 /*報錯:Protocol 'Container' can only be used as a generic 
constraint because it has Self or associated type requirements*/
    print("遵守此協(xié)議")
} else {
    print("不遵守此協(xié)議")
}
if array is Int_Container {
    print("遵守此協(xié)議")
} else {
    print("不遵守此協(xié)議")
}

上述1,2的示例中揩瞪,帶有關(guān)聯(lián)類型的協(xié)議,不管是作為函數(shù)的參數(shù)類型或?qū)ο蟮膶傩灶愋吐ǔ澹€是單獨判斷某個類型是否遵守此協(xié)議李破,都會報錯:Protocol 'Container' can only be used as a generic constraint because it has Self or associated type requirements。編譯器告訴我們Container協(xié)議有Self或關(guān)聯(lián)類型的要求壹将,因此它只能被用來作為泛型的約束嗤攻。
關(guān)于Self的提示:系統(tǒng)庫為協(xié)議提供了Self關(guān)聯(lián)類型,默認(rèn)指向了實現(xiàn)此協(xié)議的類型诽俯。

//系統(tǒng)的`Equatable `協(xié)議
public protocol Equatable {
    static func == (lhs: Self, rhs: Self) -> Bool
}
//實際實現(xiàn)
class Person : Equatable {
    //默認(rèn)關(guān)聯(lián)`Self`到`Person`
    static func == (lhs: Person, rhs: Person) -> Bool {
        return lhs.name == rhs.name
    }
    var name : String?
    var age : String?
}

若我們的Int_Container協(xié)議定義中使用了關(guān)聯(lián)類型Self妇菱,編譯器依舊會報此錯誤。

protocol Int_Container {
    mutating func append(_ item : Int)
    var count : Int{get}
    subscript(i:Int)->Int{get}
    static func testCompare(l:Self,r:Self)->Bool
}

對比泛型和協(xié)議的關(guān)聯(lián)類型:

  • 泛型:使用占位符類型完成泛型類型方法的實現(xiàn)暴区,泛型的實際類型由使用此泛型類型者指定闯团。即:使用時指定實際類型。
  • 關(guān)聯(lián)類型:使用占位符類型完成協(xié)議方法的定義仙粱,關(guān)聯(lián)類型的實際類型由實現(xiàn)此協(xié)議者指定房交,即:實現(xiàn)時指定實際類型。

關(guān)聯(lián)類型的協(xié)議用作泛型的約束舉例:

//①
struct TempStruct<T:Container> {
    let title : String = "關(guān)聯(lián)類型的協(xié)議用作泛型類型的約束:代替`T`的實際類型必須遵守`Container`協(xié)議"
    func showYourMagic(_ para : T) -> Void {
        print(para)
    }
}
//②
func showYourMagic<T>(_ para : T) -> Void {
    print(para)
}
showYourMagic("展示魔法")

總結(jié):帶有關(guān)聯(lián)類型的協(xié)議只能用作泛型的約束伐割。

添加約束到關(guān)聯(lián)類型

可以為協(xié)議中的關(guān)聯(lián)類型添加類型約束候味,以要求符合條件的類型滿足這些約束。

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

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

在關(guān)聯(lián)類型的約束中使用協(xié)議隔心,協(xié)議可以作為協(xié)議要求的一部分出現(xiàn)白群。(當(dāng)前協(xié)議可作為關(guān)聯(lián)類型的協(xié)議要求出現(xiàn))。
Container協(xié)議舉例济炎,定義協(xié)議SuffixableContainer繼承自Container川抡,實現(xiàn)功能:實現(xiàn)此協(xié)議類型的實例,需要截取它后綴一定長度,組成新的實例崖堤。

//協(xié)議定義
//定義繼承協(xié)議
protocol SuffixableContainer : Container {
    
    /*新構(gòu)建的關(guān)聯(lián)類型`suffix`約束條件有兩個:
     1.實現(xiàn)此協(xié)議時指定的`suffix`的類型必須是實現(xiàn)`SuffixableContainer`協(xié)議的類型
     2.此`suffix`占位的容器類型的存儲項類型`Item`必須與當(dāng)前實現(xiàn)此協(xié)議的存儲項保持一致侍咱。
     */
    associatedtype suffix : SuffixableContainer where suffix.Item == Item
    
    /*`item`關(guān)聯(lián)類型的實際類型由泛型類型的占位類型決定。
     此方法必須確保`String`類型的容器密幔,截取的后綴楔脯,重組后的容器仍然是`String`類型的*/
    func suffix(_ size : Int) -> suffix
    
}
//實現(xiàn)
extension Stack : SuffixableContainer {
    func suffix(_ size: Int) -> Stack {
        var result = Stack()
        for index in (count-size)..<count {
            result.append(self[index])
        }
        return result
    }
}
//調(diào)用
var stack_int = Stack<Int>()
stack_int.push(7)
stack_int.push(3)
stack_int.push(2)
stack_int.append(4)
let suffix_int = stack_int.suffix(3)
print(stack_int,suffix_int)//3 2 4

上述示例,SuffixableContainer協(xié)議的關(guān)聯(lián)類型suffix使用了SuffixableContainer協(xié)議進行約束胯甩。
基于suffix(_ : ) -> suffix此方法必須確保String類型的特定容器昧廷,截取的后綴,重組后的容器仍然是String類型的此容器偎箫。解釋一下關(guān)于關(guān)聯(lián)類型suffix的約束:

  • 實現(xiàn)此協(xié)議時指定的suffix的類型必須是實現(xiàn)SuffixableContainer協(xié)議的類型木柬。
  • suffix占位的容器類型的存儲項類型Item必須與當(dāng)前實現(xiàn)此協(xié)議類型(調(diào)用類型)的存儲項保持一致。item關(guān)聯(lián)類型的實際類型由泛型類型的占位類型決定淹办。

泛型的where閉包

where閉包能要求關(guān)聯(lián)類型必須遵守某個特定的協(xié)議眉枕,或特定的類型參數(shù)與關(guān)聯(lián)類型必須相等。where閉包以where關(guān)鍵字開始怜森,后跟關(guān)聯(lián)類型的約束或類型參數(shù)與關(guān)聯(lián)類型之間的相等關(guān)系。我們可以在類型或函數(shù)主體的大括號前寫一個通用的where子句來設(shè)置我們的約束副硅。
以匹配兩個容器是否相等的功能舉例來闡述姥宝。

func twoContainerIsEqual<C1:Container,C2:Container>(_ someContainer : C1 , _ anotherContainer : C2) -> Bool where C1.Item == C2.Item , C2.Item : Equatable {
    /*where閉包對于關(guān)聯(lián)類型的約束:1.容器元素類型一致恐疲,
      2.元素的類型遵守`Equatable`協(xié)議*/
    if someContainer.count != anotherContainer.count {
        return false
    }
    for i in 0..<someContainer.count {
        if someContainer[i] != anotherContainer[i] {
            return false
        }
    }
    return true
}

使用where閉包擴展泛型

1.where閉包可以作為泛型擴展的一部分糜烹。

extension Stack where Element: Equatable {
    func isTop(_ item: Element) -> Bool {
        guard let topItem = items.last else {
            return false
        }
        return topItem == item
    }
}
//調(diào)用
struct NotEquatable {
}
var notEquatableStack = Stack<NotEquatable>()
let notEquatableValue = NotEquatable()
notEquatableStack.push(notEquatableValue)

/* Error:Argument type 'NotEquatable' 
 does not conform to expected type 'Equatable'*/
notEquatableStack.isTop(notEquatableValue)

2.where閉包可以作為協(xié)議擴展的一部分茸炒。

/*
協(xié)議通過擴展可以為遵守協(xié)議的類型提供方法,初始化壁公,下
標(biāo)和計算屬性的實現(xiàn)感论。這一點允許我們?yōu)閰f(xié)議本身定義行
為,而不是基于遵守協(xié)議的每個類型
*/
extension Container where Item: Equatable {
//若`startsWith`函數(shù)名不與`container`中要求重名紊册,則`startsWith`便是為遵守此協(xié)議的類型增加了新的方法比肄。
    func startsWith(_ item: Item) -> Bool {
        return count >= 1 && self[0] == item
    }
}

3.where閉包快耿,可以要求Container協(xié)議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)
    }
}

關(guān)聯(lián)類型使用泛型 where閉包芳绩。

關(guān)聯(lián)類型上使用泛型where子句掀亥。
例如,為Container 協(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
}
//構(gòu)建迭代器
struct Iterator<T> : IteratorProtocol{
    
    var stack : Stack<T>
    var count = 0
    
    init(_ stack : Stack<T>) {
        self.stack = stack
    }
    
    typealias Element = T

    mutating func next() -> T? {
        let next = stack.count - 1 - count
        guard next >= 0 else {
            return nil
        }
        count += 1
        return stack[next]
    }
}
//我們的泛型`Stack`需要實現(xiàn)`Sequence`協(xié)議
struct Stack<Element> : Container,Sequence {
    
    //container只能用作泛型約束搪花。
    var items = [Element]()
    mutating func push(_ item:Element){
        items.append(item)
    }
    mutating func pop(_ item:Element) -> Element {
        return items.removeLast()
    }
    //實現(xiàn)協(xié)議
    typealias Item = Element
    //自動提示為`Element`
    mutating func append(_ item: Element) {
        push(item)
    }
    var count: Int {
        items.count
    }
    subscript(i: Int) -> Element {
        items[i]
    }
    //迭代器的實現(xiàn)
    typealias IteratorType = Iterator<Element>
    func makeIterator() -> IteratorType {
        return Iterator.init(self)
    }
}
//調(diào)用
var stack_int = Stack<Int>()
stack_int.push(7)
stack_int.push(3)
stack_int.push(2)
stack_int.append(4)
for item in stack_int {
    print(item)
}
//輸出:
4
2
3
7

Iterator : IteratorProtocol where Iterator.Element == Item要求Iterator必須遍歷與容器的元素具有相同類型的元素,而不管迭代器的類型嘹害。

泛型下標(biāo)

下標(biāo)可以泛型撮竿,也可以包括泛型where子句,下標(biāo)后的尖括號內(nèi)寫占位符類型名稱笔呀,并在下標(biāo)正文的左花括號前寫泛型where子句幢踏。

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
    }
}

Indices.Iterator.Element == Int保證了序列中的索引與用于容器的索引具有相同的類型。即:意味著為索引參數(shù)傳遞的值是整數(shù)序列许师。

參考資料:
swift 5.1官方編程指南

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末惑折,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子枯跑,更是在濱河造成了極大的恐慌,老刑警劉巖白热,帶你破解...
    沈念sama閱讀 212,029評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件敛助,死亡現(xiàn)場離奇詭異,居然都是意外死亡屋确,警方通過查閱死者的電腦和手機纳击,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,395評論 3 385
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來攻臀,“玉大人焕数,你說我怎么就攤上這事∨傩ィ” “怎么了堡赔?”我有些...
    開封第一講書人閱讀 157,570評論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長设联。 經(jīng)常有香客問我善已,道長,這世上最難降的妖魔是什么离例? 我笑而不...
    開封第一講書人閱讀 56,535評論 1 284
  • 正文 為了忘掉前任换团,我火速辦了婚禮,結(jié)果婚禮上宫蛆,老公的妹妹穿的比我還像新娘艘包。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 65,650評論 6 386
  • 文/花漫 我一把揭開白布想虎。 她就那樣靜靜地躺著卦尊,像睡著了一般。 火紅的嫁衣襯著肌膚如雪磷醋。 梳的紋絲不亂的頭發(fā)上猫牡,一...
    開封第一講書人閱讀 49,850評論 1 290
  • 那天,我揣著相機與錄音邓线,去河邊找鬼淌友。 笑死,一個胖子當(dāng)著我的面吹牛骇陈,可吹牛的內(nèi)容都是我干的震庭。 我是一名探鬼主播,決...
    沈念sama閱讀 39,006評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼你雌,長吁一口氣:“原來是場噩夢啊……” “哼器联!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起婿崭,我...
    開封第一講書人閱讀 37,747評論 0 268
  • 序言:老撾萬榮一對情侶失蹤拨拓,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后氓栈,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體渣磷,經(jīng)...
    沈念sama閱讀 44,207評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,536評論 2 327
  • 正文 我和宋清朗相戀三年授瘦,在試婚紗的時候發(fā)現(xiàn)自己被綠了醋界。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,683評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡提完,死狀恐怖形纺,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情徒欣,我是刑警寧澤逐样,帶...
    沈念sama閱讀 34,342評論 4 330
  • 正文 年R本政府宣布,位于F島的核電站帚称,受9級特大地震影響官研,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜闯睹,卻給世界環(huán)境...
    茶點故事閱讀 39,964評論 3 315
  • 文/蒙蒙 一戏羽、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧楼吃,春花似錦始花、人聲如沸妄讯。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,772評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽亥贸。三九已至,卻和暖如春浇垦,著一層夾襖步出監(jiān)牢的瞬間炕置,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,004評論 1 266
  • 我被黑心中介騙來泰國打工男韧, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留朴摊,地道東北人。 一個月前我還...
    沈念sama閱讀 46,401評論 2 360
  • 正文 我出身青樓此虑,卻偏偏與公主長得像甚纲,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子朦前,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,566評論 2 349

推薦閱讀更多精彩內(nèi)容

  • 本章將會介紹 泛型所解決的問題泛型函數(shù)類型參數(shù)命名類型參數(shù)泛型類型擴展一個泛型類型類型約束關(guān)聯(lián)類型泛型 Where...
    寒橋閱讀 631評論 0 2
  • 泛型(Generics) 泛型代碼允許你定義適用于任何類型的,符合你設(shè)置的要求的,靈活且可重用的 函數(shù)和類型介杆。泛型...
    果啤閱讀 668評論 0 0
  • 泛型的概念 泛型代碼可根據(jù)自定義需求,寫出適用于任何類型韭寸、靈活且可重用的函數(shù)和類型春哨,避免重復(fù)的代碼,用一種清晰和抽...
    伯wen閱讀 396評論 0 2
  • 泛型代碼使您能夠編寫靈活的恩伺、可重用的函數(shù)和類型悲靴,這些函數(shù)和類型可以使用任何類型,取決于您定義的需求莫其。您可以編寫避免...
    微笑中的你閱讀 427評論 0 0
  • 日更文/83 【小文月計劃行動營打卡】18/22 【正正-每日能量六步驟】 今天日總結(jié) 【今日三件事】 1.事件:...
    正正成長筆記閱讀 485評論 0 2