通过在Swift 2.0中添加协议扩展,似乎协议基本上已经成为Java / C#抽象类。我可以看到的唯一区别是抽象类仅限于单个继承,而Swift类型可以符合任何数量的协议。
这是对Swift 2.0中协议的正确理解,还是有其他区别?
有几个重要的区别…
协议扩展可以与值类型以及类一起使用。
值类型是结构和枚举。例如,你可以扩展IntegerArithmeticType到增加isPrime财产所有整数类型(UInt8,Int32,等)。或者,您可以将协议扩展与结构扩展结合使用,以向多个现有类型添加相同的功能—例如,对CGPoint和都添加向量算术支持CGVector。
IntegerArithmeticType
isPrime
UInt8
Int32
CGPoint
CGVector
Java和C#在语言级别上实际上没有用户可创建/可扩展的“普通旧数据”类型,因此此处实际上没有类似物。Swift大量使用值类型- 与ObjC,C#和Java不同,在Swift中,甚至集合都是写时复制值类型。这有助于解决有关可变性和线程安全性的许多问题,因此,使自己的值类型而不是始终使用类可以帮助您编写更好的代码。(请参阅WWDC15 中的使用Swift在值类型中构建更好的应用程序。)
例如,您可以具有一个扩展,该扩展CollectionType仅在集合的基础元素类型满足某些条件时才向其中添加方法。这是找到集合最大元素的方法- 在数字或字符串的集合上显示此属性,但是在UIViews(不是Comparable)的集合上,此属性不存在。
CollectionType
UIView
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 在Swift中的面向协议的编程中所述,您可以使用它来选择哪些功能应该是可以由采用协议的客户端自定义的替代点,以及哪些功能应该始终是该协议提供的标准功能。
As you’ve noted already, protocol conformance is a form of multiple inheritance. If your type conforms to multiple protocols, and those protocols have extensions, your type gains the features of all extensions whose constraints it meets.
You might be aware of other languages that offer multiple inheritance for classes, where that opens an ugly can of worms because you don’t know what can happen if you inherit from multiple classes that have the same members or functions. Swift 2 is a bit better in this regard:
Conflicts between protocol extensions are always resolved in favor of the most constrained extension. So, for example, a method on collections of views always wins over the same-named method on arbitrary collections (which in turn wins over the same-named methods on arbitrary sequences, because CollectionType is a subtype of SequenceType).
SequenceType
Calling an API that’s otherwise conflicting is a compile error, not a runtime ambiguity.
A protocol definition can require that types adopting the protocol must implement a property:
protocol Named { var name: String { get } // or { get set } for readwrite }
A type adopting the protocol can choose whether to implement that as a stored property or a computed property, but either way, the adopting type must declare its implementation the property.
An extension can implement a computed property, but an extension cannot add a stored property. This is true whether it’s a protocol extension or an extension of a specific type (class, struct, or enum).
By contrast, a class can add stored properties to be used by a subclass. And while there are no language features in Swift to enforce that a superclass be abstract (that is, you can’t make the compiler forbid instance creation), you can always create “abstract” superclasses informally if you want to make use of this ability.