Swift 2.0协议扩展和Java/C#抽象类之间有区别吗?

jjo*_*son 14 protocols swift swift2

通过在Swift 2.0中添加协议扩展,似乎协议基本上已成为Java/C#抽象类.我能看到的唯一区别是抽象类限制为单继承,而Swift类型可以符合任意数量的协议.

这是对Swift 2.0协议的正确理解,还是存在其他差异?

ric*_*ter 45

有几个重要的区别......

协议扩展可以使用值类型和类.

值类型是结构和枚举.例如,你可以扩展IntegerArithmeticType到增加isPrime财产所有整数类型(UInt8,Int32,等).或者你可以将协议扩展与结构扩展相同的功能添加到多个现有各类-说,加入这两个向量算法的支持CGPointCGVector.

Java和C#在语言级别上并不真正具有用户可创建/可扩展的"普通旧数据"类型,因此这里并没有真正的模拟.Swift使用了很多值类型 - 与ObjC,C#和Java不同,在Swift中甚至集合都是写时复制值类型.这有助于解决许多关于可变性和线程安全性的问题,因此创建自己的值类型而不是总是使用类可以帮助您编写更好的代码.(请参阅WWDC15 中的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
    }
}
Run Code Online (Sandbox Code Playgroud)

(帽子提示:这个例子刚刚出现在优秀的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"
Run Code Online (Sandbox Code Playgroud)

正如Apple 在Swift中面向协议编程中所述,您可以使用它来选择哪些函数应该是覆盖点,这些函数可以由采用协议的客户端自定义,哪些函数应该始终是协议提供的标准功能.

类型可以从多个协议获得扩展功能.

正如您已经注意到的,协议一致性是多重继承的一种形式.如果您的类型符合多个协议,并且这些协议具有扩展,则您的类型将获得其满足约束的所有扩展的功能.

您可能知道为类提供多重继承的其他语言,这会打开一个丑陋的蠕虫,因为您不知道如果从具有相同成员或函数的多个类继承会发生什么.Swift 2在这方面要好一些:

  • 协议扩展之间的冲突总是被解决,以支持最受约束的扩展.因此,例如,关于视图集合的方法总是胜过任意集合上的同名方法(这反过来胜过任意序列上的相同命名方法,因为CollectionType它是子类型SequenceType).

  • 调用其他方面存在冲突的API是编译错误,而不是运行时模糊.

协议(和扩展)无法创建存储.

协议定义可能要求采用协议的类型必须实现属性:

protocol Named {
    var name: String { get } // or { get set } for readwrite 
}
Run Code Online (Sandbox Code Playgroud)

采用该协议的类型可以选择是将其实现为存储属性还是计算属性,但无论采用哪种方式,采用类型都必须将其实现声明为属性.

扩展可以实现计算属性,但扩展不能添加存储的属性.无论是协议扩展还是特定类型(类,结构或枚举)的扩展,都是如此.

相比之下,类可以添加子类使用的存储属性.虽然Swift中没有语言功能来强制超类是抽象的(也就是说,你不能让编译器禁止实例创建),但如果你想利用这个能力,你总是可以非正式地创建"抽象"超类. .

  • 哇,很好的答案.在我看来,约束协议扩展的能力是你提到的抽象类的最大和最有用的区别. (2认同)
  • 静态分派意味着当你调用一个存在于一个地方的方法时,它在编译时就知道你会得到哪一个.动态调度意味着所述决定在运行时发生.上面链接的NSBlog文章是获取更多信息的好地方. (2认同)