【发布时间】:2015-06-19 16:44:08
【问题描述】:
随着 Swift 2.0 中协议扩展的加入,协议似乎基本上变成了 Java/C# 抽象类。我能看到的唯一区别是抽象类仅限于单一继承,而 Swift 类型可以符合任意数量的协议。
这是对 Swift 2.0 中协议的正确理解,还是有其他区别?
【问题讨论】:
随着 Swift 2.0 中协议扩展的加入,协议似乎基本上变成了 Java/C# 抽象类。我能看到的唯一区别是抽象类仅限于单一继承,而 Swift 类型可以符合任意数量的协议。
这是对 Swift 2.0 中协议的正确理解,还是有其他区别?
【问题讨论】:
有几个重要的区别...
值类型是结构和枚举。例如,您可以扩展 IntegerArithmeticType 以将 isPrime 属性添加到所有整数类型(UInt8、Int32 等)。或者,您可以将协议扩展与结构扩展结合起来,为多种现有类型添加相同的功能——例如,为CGPoint 和CGVector 添加向量算术支持。
Java 和 C# 在语言级别上并没有真正的用户可创建/可扩展的“普通旧数据”类型,因此这里没有真正的类比。 Swift 大量使用值类型——与 ObjC、C# 和 Java 不同,在 Swift 中,甚至集合都是写入时复制的值类型。这有助于解决许多关于可变性和线程安全性的问题,因此创建自己的值类型而不是总是使用类可以帮助您编写更好的代码。 (参见 WWDC15 中的 Building Better Apps with Value Types in Swift。)
例如,您可以有一个扩展,仅当集合的基础元素类型满足某些条件时才向CollectionType 添加方法。这是一个找到集合的最大元素的方法——在数字或字符串的集合上,此属性会显示,但在 UIViews(不是 Comparable)的集合上,此属性不会不存在。
extension CollectionType where Self.Generator.Element: Comparable {
var max: Self.Generator.Element {
var best = self[self.startIndex]
for elt in self {
if elt > best {
best = elt
}
}
return best
}
}
(提示:这个例子刚刚出现在优秀的NSBlog 上。)
在这些 WWDC15 演讲中还有一些更好的受限协议扩展示例(可能还有更多,但我还没有关注视频):
抽象类——在任何语言中,包括 ObjC 或 Swift,它们是一种编码约定而不是一种语言特性——按照类继承线工作,因此所有子类都继承抽象类功能,无论它是否有意义。
这个更令人头疼,但如果使用得当,它会非常强大。这是一个基本示例(同样来自NSBlog):
protocol P {
func a()
}
extension P {
func a() { print("default implementation of A") }
func b() { print("default implementation of B") }
}
struct S: P {
func a() { print("specialized implementation of A") }
func b() { print("specialized implementation of B") }
}
let p: P = S()
p.a() // -> "specialized implementation of A"
p.b() // -> "default implementation of B"
正如 Apple 在 Protocol-Oriented Programming in Swift 中指出的那样,您可以使用它来选择哪些函数应该是可以由采用协议的客户端自定义的覆盖点,以及哪些函数应该始终是协议提供的标准功能。
正如您已经注意到的,协议一致性是多重继承的一种形式。如果您的类型符合多个协议,并且这些协议具有扩展,则您的类型将获得其满足约束的所有扩展的特性。
您可能知道为类提供多重继承的其他语言,这会打开一个丑陋的蠕虫罐,因为您不知道如果从具有相同成员或函数的多个类继承会发生什么。 Swift 2 在这方面要好一些:
协议扩展之间的冲突是always resolved in favor of the most constrained extension。因此,例如,视图集合上的方法总是胜过任意集合上的同名方法(这反过来又胜过任意序列上的同名方法,因为CollectionType 是SequenceType 的子类型)。
调用存在冲突的 API 是编译错误,而不是运行时歧义。
协议定义可以要求采用该协议的类型必须实现一个属性:
protocol Named {
var name: String { get } // or { get set } for readwrite
}
采用该协议的类型可以选择将其实现为存储属性还是计算属性,但无论哪种方式,采用该协议的类型都必须将其实现声明为该属性。
一个扩展可以实现一个computed属性,但是一个扩展不能添加一个stored属性。无论是协议扩展还是特定类型(类、结构或枚举)的扩展都是如此。
相比之下,一个类可以添加存储的属性以供子类使用。虽然 Swift 中没有语言特性强制超类是抽象的(也就是说,你不能让编译器禁止实例创建),你总是可以非正式地创建“抽象”超类,如果你想利用这个能力。
【讨论】: