Swift 泛型

前言

  • 在 Swift 语言中,泛型可以说是用的最广最强大的特性之一,因为在 Swift 语言本身的语言底层大量的使用了泛型。

  • 泛型使得同样的操作可以应用于不同的数据类型。

    • 泛型编程的实现是我们程序在另一种抽象层次上的提升。
    • 类是现实世界事物的抽象,而泛型则是现实世界行为的抽象。

1、节点泛型

  • Swift 中的泛型同其它语言相同,用一对尖括号 < > 来声明泛型,尖括号中通常使用 TUV 等这样的大写字母来表示 “节点” 类型。

  • 使用泛型作为参数的函数叫做泛型函数。

    • 泛型函数在声明时使用节点类型命名来替代实际的类型名,并在泛型函数名后面插入节点类型的声明。
    • 节点类型在定义时不表示任何具体类型,在函数被调用时会根据传入的实际类型来指定自身的类型。

      func 函数名<T>(参数名1: T, 参数名2: T, ...) -> T {
      
          函数体 .....
      
          return 返回值
      }
  • 1)如果函数的列表中只有一个字母,如 T,虽然具体的类型不需要指定,但是函数中每个节点类型的参数(函数参数或返回值类型)必须是相同类型的。

    func show<T>(para1: T, para2: T) {
        print("\(para1)" + " \(para2)")
    }
    // 在调用时两个参数必须是相同的
    show(para1: 1, para2: 2)                        // 1 2
    show(para1: "xiaoming", para2: "xiaobai")       // xiaoming xiaobai
  • 2)如果要定义多个不同类型的泛型,则需要在尖括号中加入多个节点 <T, U, V ...>

    func show<T, U>(para1: T, para2: U) {
        print("\(para1)" + " \(para2)")
    }
    // 在调用时两个参数可以不同
    show(para1: "xiaoming", para2: 18) // xiaoming 18
  • 3)你还可以对节点进行一些限制,比如要求泛型遵守某些协议。

    // Swift 中数组的判等函数
    public func ==<Element: Equatable>(lhs: [Element], rhs: [Element]) -> Bool {...} 
    • Element 是使用节点声明的,它代表一个泛型,可以看到这里的泛型名是 Element,相比上面的 TUV 等要长的多。这是因为此处的 Element 不仅仅是一个占位符的作用,它还声明了这个泛型代表数组中的元素类型,具有具体的意义。
  • 4)有时候节点中的泛型需要有更多的限制,需要使用 where 子句来补充约束条件。

    • 在 Swift 3.0 之前。

      func anyCommonElements<T: SequenceType, U: SequenceType where
          T.Generator.Element: Equatable,
          T.Generator.Element == U.Generator.Element>(lhs: T, _ rhs: U) -> Bool {
          ...
      }
    • 在 Swift 3.0 及之后 where 子句被移动到了参数列表的后面。

      func anyCommonElements<T: SequenceType, U: SequenceType>(lhs: T, _ rhs: U) -> Bool 
          where
              T.Generator.Element: Equatable,
              T.Generator.Element == U.Generator.Element{
          ...
      }

2、泛型协议

  • 1)除了节点式声明泛型外,还有其它方式声明一个泛型,比如使用关键字 associatedtype(关联类型)(Swift 3.0 之前是 typealias 关键字)。

    protocol SomeProtocol {
    
        associatedtype Element
    
        func elementMethod1(element: Element)
        func elementMethod2(element: Element)
    }
    • 这里虽然没有出现节点语法,但上面的协议确实是个不折不扣的泛型协议,Element 起到了占位符的作用,指示了某种类型。
    • 根据协议的规则,协议 SomeProtocol 的遵守者必须实现上面两个方法,Element 隐式的约束了两个方法的参数必须是相同类型的。
    • 不用刻意指定 Element 的具体类型,编译器会根据实现方法时传入的参数类型确定 Element 的具体类型。
    • 在实现的时候不能直接用 ElementElement 只能存在于具体实现之前。泛型协议依靠遵守协议中协议方法的的具体实现来明确泛型的类型。
    • 协议的遵守者如果遵守了多个协议,那么这些协议的关联类型名称不能重复,否则编译器会报错。

      struct TestStruct: SomeProtocol {
      
          func elementMethod1(element: String) {
              print("elementFromMethod1: \(element)")
          }
      
          func elementMethod2(element: String) {
              print("elementFromMethod2: \(element)")
          }
      }
      TestStruct().elementMethod1(element: "qwert")   // elementFromMethod1: qwert
      TestStruct().elementMethod2(element: "asdfg")   // elementFromMethod2: asdfg
  • 2)类似于 associatedtype 的还有 Self 关键字,代表了协议遵守者本身的的类型,适用于 “比较” 这类方法,其必须传入另一个相同类型的参数才有意义。

    protocol CanCompare {
    
        func isBigger(other: Self) -> Bool
    }
    struct BoxInt: CanCompare {
    
        var intValue: Int
    
        func isBigger(other: BoxInt) -> Bool {
            return self.intValue > other.intValue
        }
    }
    BoxInt(intValue: 3).isBigger(other: BoxInt(intValue: 2))    // true

3、泛型对象

  • 关联类型和 Self 关键字都是在协议层面的泛型,此外还有对象层面的泛型,比如我们常用的数组就是对象层面的泛型定义的,效果相同。

  • 如果不使用协议,一个泛型对象风格的结构体定义如下。

    struct TestStruct<T: Comparable> {
    
        func elementMethod1(element: T) {
            print("elementFromMethod1: \(element)")
        }
    
        func elementMethod2(element: T) {
            print("elementFromMethod2: \(element)")
        }
    }
    • 泛型应该用在声明中,调用时版本中的泛型已经被 “特化” 成具体的泛型。泛型对象通过构造器初始化时明确明确泛型的类型,这些类型都是具体的。
    • 如果在实现时 “嵌套” 一个泛型,那么会导致泛型无法特化。比如数组本身是泛型的,在声明数组类型时传入了另一个泛型,那么你将无法初始化该数组。

      let test = TestStruct<Int>()
      test.elementMethod1(1)

4、泛型方法

  • 方法中的泛型使用节点表示法,作用域只在本方法内。

    struct TestStruct {
    
        func elementMethod1<T: Comparable>(element: T) {
            print("elementFromMethod1: \(element)")
        }
    
        func elementMethod2<T: Comparable>(element: T) {
            print("elementFromMethod2: \(element)")
        }
    }
    • 这样同一个实例的相同方法就可以接受不同的参数类型了。

      let test = TestStruct()
      test.elementMethod1(element: 1)
      test.elementMethod1(element: "abc")

5、协议中的 where 关键字

  • where 关键字可以与 for-in 循环配合实现筛选,where 同样可以用在协议扩展中,使得定义在协议扩展中的方法可以对某些遵守者 “隐身”,而对另一些遵守者 “可见”。

  • 协议的遵守者如果遵守了多个协议,那么这些协议的关联类型名称不能重复,否则编译器会报错。

    protocol SomeProtocol {
    
        associatedtype OwnElement
    
        func elementMethod1(element: OwnElement)
        func elementMethod2(element: OwnElement)
    }
  • 1)where 关键字可以与 Self 关键字配合,在协议扩展中定义一个新方法,指定当协议的遵守者是集合类型时,必须打印出遵守者的元素个数。

    extension SomeProtocol where Self: Collection {
    
        func showCount() {
            print(self.count)
        }
    }
    • where 的限制是强制的,他是一个编译器强制的开关。虽然在协议的扩展中并不能知晓会有哪些对象遵守了该协议,但是一旦使用了 where,那么就表明在扩展的作用域中 where 后面的语句默认实现了。
    • Array 类型已经遵守了 Collection 协议,所以如果让 Array 同时遵守 SomeProtocol,那么它就能获得 showCount 方法。

      extension Array: SomeProtocol {
      
          func elementMethod1(element: String) {
              print("elementFromMethod1: \(element)")
          }
      
          func elementMethod2(element: String) {
              print("elementFromMethod2: \(element)")
          }
      }
      [1, 2, 3].showCount()       // 3
  • 2)where 所指定的力度范围还可以继续缩小,可以使用 where 限定协议中的关联类型。

    extension SomeProtocol where OwnElement: SignedNumeric {
    
        func showElement() {
            print(OwnElement.self)
        }
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 在 Swift 中,泛型的 `where` 语句用于限制泛型类型。它可以在定义泛型函数或类型时使用。语法如下: ``` where 泛型别名 : 限制条件 ``` 例如: ``` func findIndex<T: Equatable>(of valueToFind: T, in array:[T]) -> Int? { for (index, value) in array.enumerated() { if value == valueToFind { return index } } return nil } ``` 在上面的例子中,泛型别名为T,限制条件为T: Equatable,意思是T必须符合Equatable协议 还可以指定多个限制条件 ``` func test<T>(_ value: T) where T: Equatable, T: Hashable { } ``` 这里的限制条件为T: Equatable, T: Hashable,意思是T必须符合Equatable和Hashable协议 ### 回答2: 在Swift中,泛型的where语句用于对泛型进行条件限制,以满足特定需求。使用where语句可以对泛型类型进行约束,使其满足特定的类型要求。 在泛型函数中,我们可以使用where语句来限制泛型参数的类型。例如,我们可以声明一个泛型函数,其中泛型参数必须遵循某个协议或满足某种特定的条件。下面是一个示例: ``` func process<T>(data: T) where T: Equatable { // 在这里操作泛型参数data,比如进行相等比较等操作 } ``` 在上面的示例中,我们限制了泛型参数T必须遵循Equatable协议,即具有相等比较能力。这样,在函数内部就可以使用相等比较操作符进行操作。 除了在泛型函数中使用where语句外,也可以在泛型类型和协议声明中使用where语句对泛型参数进行进一步的限制。例如: ``` protocol Container { associatedtype Item func addItem(item: Item) } struct MyContainer<T>: Container where T: Equatable { typealias Item = T func addItem(item: Item) { // 在这里添加item到容器中 } } ``` 在上面的示例中,我们定义了一个Container协议,其中关联类型Item表示容器中的元素类型。然后,我们通过where语句限制了泛型参数T必须满足Equatable协议,以确保容器中的元素可以进行相等比较。 总结来说,在Swift中,泛型的where语句可以用于泛型函数、泛型类型和协议的声明中,用于对泛型参数进行类型约束和条件限制,以满足特定需求。 ### 回答3: 在Swift中,泛型的where语句用于为泛型类型或泛型函数添加额外的约束。它允许我们在使用泛型时对其类型参数进行更详细的限制。 泛型的where语句可以使用在两个地方:泛型函数和泛型类型。 对于泛型函数,我们可以使用where语句来添加类型约束。例如,我们可以声明一个泛型函数来交换两个变量的值: func swap<T>(a: inout T, b: inout T) where T: Equatable { if a == b { return } let temp = a a = b b = temp } 在上面的代码中,我们使用了where语句来添加了一个类型约束,即T必须遵循Equatable协议。这意味着只有那些可比较相等的类型才能调用这个函数。 对于泛型类型,我们可以使用where语句来添加更多的类型约束。例如,我们可以声明一个泛型结构体,它的关联类型必须遵循特定协议: struct Container<T> where T: CustomStringConvertible { var items: [T] func printItems() { for item in items { print(item.description) } } } 在上面的代码中,我们使用了where语句来添加了一个类型约束,即关联类型T必须遵循CustomStringConvertible协议。这样,我们就可以在printItems方法中使用item的description属性。 总之,泛型的where语句在Swift中用于为泛型类型或泛型函数添加额外的约束。通过使用where语句,我们可以对类型参数进行更详细的限制,以满足特定的需求。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值