每个优秀的程序员都知道使用“神奇”值是不好的。大多数语言都支持枚举类型,这样可以更安全地表现一组同类型的离散值。
Swift通过“关联值”的概念进一步拓展了枚举。下面的枚举值自己还与其他枚举值相关联:
enum Optional<T> {
case None
case Some(T)
}
在某些语言中,这被称为“标签联合(tagged unions)”(或可辨识联合)。它是一种特殊的数据结构,够存储一组“不同但是固定”的类型中某个类型的对象[1],通过标签来表示具体哪一个类型正在被使用。在Swift中,这个标签就是枚举成员(case
语句)。
获取管理值的唯一方法是通过switch
或if case let
。和“哨兵值”不同的是,你必须显式地检查并解封那些包装在Optional
类型中的值。
Swift版本的find
方法叫indexOf()
,它返回的是Optional<Index>
而不是直接的Index
。这是通过协议拓展实现的:
extension CollectionType where Generator.Element: Equatable {
func indexOf(element: Generator.Element) -> Optional<Index> {
for idx in self.indices {
if self[idx] == element{
return .Some(idx)
}
}
// 没找到,返回 .None
return .None
}
}
由于可选类型非常基础,所以Swift提供了很多语法支持,让代码更加整洁。比如Optional<Index>
可以写成Index?
。因为可选类型实现了NilLiteralConvertible
协议,所以你可以直接写nil
而不是.None
。除此之外,因为非可选类型的值(如idx
)如果需要的话可以自动“升级”成可选类型,所以你可以直接写return idx
而不必写return .Some(idx)
。
现在用户就不会错误的使用无效的值了:
var array = ["one", "two", "three"]
let idx = array.indexOf("four")
//触发编译错误,removeIndex方法的参数是Int类型而不是Optional<Int>
array.removeAtIndex(idx)
因此,如果返回值不为None
,你必须“解封”可选类型才能得到其中的真正值。
switch array.indexOf("four") {
case .Some(let idx):
array.removeAtIndex(idx)
case .None:
break
}
这里的switch语句用非常普通的方式写了支持可选类型的枚举代码。当枚举成员是.Some
时解封了其中的关联类型。这样写非常安全,但不是很适合阅读和书写代码。Swift2.0引入了?
后缀语法用来匹配可选类型,还引入了nil
字面量来匹配枚举成员None
:
switch array.indexOf("four") {
case let idx? :
array.removeAtIndex(idx)
case nil:
break
}
这种写法还是显得有些笨重,我们来看一看其他能根据实际用例,让可选类型的处理更加简单的写法。
#译者注:
[1]:比如根据上面的代码我们可以写:
var t = T()
var opt = Optional.Some(t)
于是opt存储的枚举成员为Some,它的关联值为t。
枚举类型中还可以有其他的枚举成员并且关联其他类型的值,但枚举类型的变量在同一时间只能存储一个枚举成员及其关联值。这也就是之前所说的“一组不同但是固定的类型中某个类型的对象”。