swift4.1 系統學習二十一 泛型

weixin_34413357發表於2018-10-29

/*
泛型

本節我們一起學習泛型這一概念。在專案中,多次用到了泛型,但是對它的理解總是不夠透徹,運用也不熟練。
趁此機會,好好熟悉一下。

什麼是泛型?
比如我們要實現一種演算法,例如搜尋、排序等,相對於傳統C語言,如果我們要針對不同的型別實現相同的演算法,‘
每種資料型別都要寫一遍,顯得乏味而又冗餘。這個時候我們就可以使用泛型,提供一種演算法的實現函式,然後
在呼叫此函式的時候在確定相關資料的型別就可以了。
*/

print("---------泛型的概念--------------")

struct A {
    /*
     這裡定義了函式calculate
     其泛型遵循了BinaryInteger協議。
     */
    static func calculate<T: BinaryInteger> (a: T, b: T) -> T {
        return a * a + b * b
    }
    
    static func calculate<T: FloatingPoint> (a: T, b: T) -> T {
        return a * a + b * b
    }
   
}

do {
    
    let i = A.calculate(a: 3, b: 4)
    print("i = \(i)")
    
    let j = A.calculate(a: 3.0, b: 4.0)
    print("j = \(j)")
}

/*
小結:
通過上面的例子可以看出來,使用泛型之後,函式定義一下子簡單了。我們不僅可以使用Int8,Int16,Int32
等整數型別,還可以使用Float、Double等浮點型別。如果沒有泛型,那麼我們要寫10多種函式實現,
是不是簡化了很多呢?
*/

// 1. 泛型基本使用
/*
swift 中,我們可以對函式以及方法使用泛型,但是不能用於閉包,不能用於協議。
無論是對函式還是對型別使用泛型,我們均可以在函式或者型別識別符號後面跟 <>,裡面放泛型形參列表。
*/

do {

// 定義一個泛型結構體,泛型形參為T
struct MyStruct<T> {
    
    /// 用泛型T定義儲存成員t
    var t: T
    
    /// 定義一個例項方法,其引數也是泛型T
    mutating func method(value: T) {
        t = value
        print("new value = \(value)")
    }
    
    /// 定義型別方法swap,是一個泛型方法,泛型形參為E
    static func swap<E>(a: inout E, b: inout E) {
        
        let tmp = a
        a = b
        b = tmp
    }
}

/*
 將MyStruct型別特化為MyStruct<Int32>,表示其泛型實參為Int32型別。
 */
var st = MyStruct<Int32>(t: 100)
// 呼叫method方法
st.method(value: 200)

// 這裡預設<Int32>,此時MyStruct將被型別推導為MyStruct<Int>。
var ss = MyStruct(t: 100)
ss.method(value: 200)

var a = 10, b = 20
// 這裡呼叫了MyStruct的swap型別方法,
// 並且其泛型被推導為Int型別。
// 這裡注意,即便我們沒有直接使用MyStruct物件例項,
// 但依然需要對MyStruct做泛型特化,
// 由於這裡不需要做特殊處理,
// 所以對MyStruct直接指定空元組即可。
MyStruct<()>.swap(a: &a, b: &b)
print("a = \(a), b = \(b)")

var x = 0.5, y = 5.0
MyStruct<()>.swap(a: &x, b: &y)
print("x = \(x), y = \(y)")

var c = 100, d = 200
MyStruct<Int>.swap(a: &c, b: &d)
print("c = \(c), d = \(d)")

var e = "a", f = "b"
MyStruct<String>.swap(a: &e, b: &f)
print("e = \(e), b = \(f)")

var arr1 = [1, 2, 3]
var arr2 = [10, 20, 30, 40]
MyStruct<[Int]>.swap(a: &arr1, b: &arr2)
print("arr1 = \(arr1), arr2 = \(arr2)")

}

/*
小結:
上述程式碼可以看出,泛型是多麼方便啊。對於交換兩個物件的值,我們只需要使用泛型,就可以寫一種演算法,針對
各個型別都可以使用了。
*/

/*
對於函式以及方法的泛型特化,我們不能顯示指定泛型實參,只能通過推導得出。
如果某一函式只有返回型別是一個泛型型別,那麼呼叫它的時候,必須顯式提供左值型別。
*/

do {

func foo<T: SignedInteger>() -> T {
    return T()
}

let a: Int16 = foo()
print("a = \(a)")

}

// 2. 型別約束
/*
swift中引入了泛型的“型別約束”,用於說明當前泛型型別必須遵循哪些協議,或者繼承自哪個類。有了這個
設定之後,我們的泛型的靈活性就大大增強了。
*/

do {

print("\n--------2. 型別約束-----------\n")

// 定義了一個泛型函式,必須遵循Comparable協議
func foo<T: Comparable>(a: T, b: T, c:T) {
    
    var minValue = min(a, b)
    minValue = min(minValue, c)
    
    var maxValue = max(a, b)
    maxValue = max(maxValue, c)
    
    let range = minValue ... maxValue
    
    print("the range is : \(range)")
}

foo(a: 10, b: 30, c: 20)

}

// 3. 泛型where從句
/*
有時我們在一個結構體或類中遵循了帶有關聯型別的協議,此時我們想實現一個泛型型別或泛型函式,使得該泛型遵循該協議,並且對該協議的泛型進行約束,那麼我們可以使用Swift中的where從句來實現。
*/

protocol Prot {
    
    // 這裡定義一個關聯型別,相當於協議中的泛型
    associatedtype ArgType
    
    func method(obj: ArgType)
}
do {
    
    print("\n-----------3. 泛型where從句---------------\n")
    
    struct MyStruct: Prot {
        
        typealias ArgType  = Int
        
        func method(obj: Int) {
            print("obj = \(obj)")
        }
    }
    
    /*
     這裡定義了泛型結構體Test,其泛型T遵循了Prot協議,使用where語句用來約束Prot協議中關聯型別
     必須遵循Comparable協議。
     */
    struct Test<T: Prot> where T.ArgType: Comparable {
        
        func method<E: Prot>(e: E) where E.ArgType == Int {
            
            print("e = \(e)")
            e.method(obj: 100)
        }
    }
    
    let test = Test<MyStruct>()
    test.method(e: MyStruct())
}

// 4. swift4.0之後對泛型特徵的補充
/*
 swift 4.0之後,我們允許對下標使用泛型。
 */

do {
    
    print("\n-----------4. swift4.0之後對泛型特徵的補充------------\n")
    
    struct Test {
        
        subscript<T: FixedWidthInteger>(index: T) -> T {
            return index + T(1)
        }
    }
    
    let a = Test()[Int8(10)]
    print("a = \(a), a type is: \(type(of: a))")
    
    let b = Test()[UInt16(600)]
    print("b = \(b), b type is: \(type(of: b))")
}

相關文章