位置:首页 > 高级语言 > Swift在线教程 > Swift关联类型

Swift关联类型

关联类型

当定义一个协议时,有的时候声明一个或多个关联类型作为协议定义的一部分是非常有用的。一个关联类型给定作用于协议部分的类型一个节点名(或别名)。作用于关联类型上实际类型是不需要指定的,直到该协议接受。关联类型被指定为typealias关键字。

关联类型行为

这里是一个Container协议的例子,定义了一个ItemType关联类型:

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

Container协议定义了三个任何容器必须支持的兼容要求:

  • 必须可能通过append方法添加一个新item到容器里;
  • 必须可能通过使用count属性获取容器里items的数量,并返回一个Int值;
  • 必须可能通过容器的Int索引值下标可以检索到每一个item。

这个协议没有指定容器里item是如何存储的或何种类型是允许的。这个协议只指定三个任何遵循Container类型所必须支持的功能点。一个遵循的类型也可以提供其他额外的功能,只要满足这三个条件。

任何遵循Container协议的类型必须指定存储在其里面的值类型,必须保证只有正确类型的items可以加进容器里,必须明确可以通过其下标返回item类型。

为了定义这三个条件,Container协议需要一个方法指定容器里的元素将会保留,而不需要知道特定容器的类型。Container协议需要指定任何通过append方法添加到容器里的值和容器里元素是相同类型,并且通过容器下标返回的容器元素类型的值的类型是相同类型。

为了达到此目的,Container协议声明了一个ItemType的关联类型,写作typealias ItemType。这个协议不会定义ItemType是什么的别名,这个信息留给了任何遵循协议的类型来提供。尽管如此,ItemType别名支持一种方法识别在一个容器里的items类型,以及定义一种使用在append方法和下标中的类型,以便保证任何期望的Container的行为是强制性的。

这里是一个早前IntStack类型的非泛型版本,适用于遵循Container协议:

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类型实现了Container协议的所有三个要求,在IntStack类型的每个包含部分的功能都满足这些要求。

此外,IntStack指定了Container的实现,适用的ItemType被用作Int类型。对于这个Container协议实现而言,定义 typealias ItemType = Int,将抽象的ItemType类型转换为具体的Int类型。

感谢Swift类型参考,你不用在IntStack定义部分声明一个具体的IntItemType。由于IntStack遵循Container协议的所有要求,只要通过简单的查找append方法的item参数类型和下标返回的类型,Swift就可以推断出合适的ItemType来使用。确实,如果上面的代码中你删除了typealias ItemType = Int这一行,一切仍旧可以工作,因为它清楚的知道ItemType使用的是何种类型。

你也可以生成遵循Container协议的泛型Stack类型:

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

这个时候,占位类型参数T被用作append方法的item参数和下标的返回类型。Swift 因此可以推断出被用作这个特定容器的ItemTypeT的合适类型。

扩展一个存在的类型为一指定关联类型

使用扩展来添加协议兼容性中有描述扩展一个存在的类型添加遵循一个协议。这个类型包含一个关联类型的协议。

Swift的Array已经提供append方法,一个count属性和通过下标来查找一个自己的元素。这三个功能都达到Container协议的要求。也就意味着你可以扩展Array去遵循Container协议,只要通过简单声明Array适用于该协议而已。如何实践这样一个空扩展,在使用扩展来声明协议的采纳中有描述这样一个实现一个空扩展的行为:

extension Array: Container {}

如同上面的泛型Stack类型一样,Array的append方法和下标保证Swift可以推断出ItemType所使用的适用的类型。定义了这个扩展后,你可以将任何Array当作Container来使用。