Kotlin 語言和 Swift 語言在一個團隊一起開發已經很久了,由于平臺的差異性,他們經常會進行一些技術上的交流(PK),「Kotlin vs. Swift」 系列就是他們在互相切磋時的語錄。內容會由簡及深,慢慢深入。
技術漫談
Swift:
Hi, Kotlin 君, Swift 4 發布了,我們今天就基于 Swift 4 的新語法來討論一下接口吧?
Kotlin:
好啊,接口對我們開發來說是個很重要的概念。設計模式中要求我們寫代碼要遵循依賴倒置原則,就是程序要依賴于抽象接口,不要依賴于具體實現,也就是要求我們要面向接口編程。
Swift:
是的,在 Swift 中,接口被稱為協議(即 Protocol ), 蘋果大大強化了 Protocol 在這門語言中的地位,整個 Swift 標準庫也是基于 Protocol 來設計的,可以說 Swift 是一門面向 protocol 編程的語言。
Kotlin:
聽起來好流比,那來說說你們是怎么定義接口的?
Swift:
我們用 Protocol 關鍵字來定義接口:
protocol SomeProtocol {
func f()
}
你們呢?
Kotlin:
我們同 Java 一樣,用 interface 關鍵字來定義接口:
interface MyInterface {
fun f()
}
Swift:
嗯,看起來就是關鍵字不一樣。你們怎么實現接口呢?
Kotlin:
一個類要實現某個接口,需要在類型名稱后加上協議名稱,中間以冒號(:)分隔:
class MyClass: MyInterface {
override fun f() {
// 具體實現
}
}
一個類或者對象可以實現一個或多個接口。實現多個接口時,各接口之間用逗號(,)分隔.
Swift:
我們也是一樣的,只是我們不需要寫 override 關鍵字,只有當子類復寫父類的方法或計算屬性時才需要用 override 修飾。另外,我們還可以通過擴展類型來實現協議:
class MyClass {
//...類的定義
}
extension MyClass: SomeProtocol {
func f() {
// 具體實現
}
}
Kotlin:
這意味著你們不用修改原有類型,就可以讓原有類型符合某個協議了,甚至可以擴展標準庫中的某個基礎類型來實現自定義的協議。這很符合開閉原則嘛。
Swift:
是啊,牛不牛 。
我們實現協議的類型除了 class 外,還可以是 struct 或 enum。
Kotlin:
Kotlin 沒有結構體的概念, enum 也可以實現接口。
來說說你們的接口中可以聲明哪些東西吧?
Swift:
我們可以在協議中聲明屬性和方法,用 var 關鍵字來聲明變量屬性,并在屬性聲明后加上 { set get } 來表示屬性是可讀可寫的,用 { get } 來表示屬性是只讀的。
協議里面聲明的屬性和方法一定是抽象的,不能有實現,由符合協議的類型來提供所有屬性和方法的實現。
Kotlin:
我們也可以聲明屬性和方法,而且 Kotlin 可以直接在接口中為屬性和方法提供默認實現:
interface MyInterface {
val prop: Int // 抽象的
val propertyWithImplementation: String
get() = "foo"
fun foo() {
print(prop)
}
}
class MyClass : MyInterface {
override val prop: Int = 29
}
Swift:
雖然我們不能在協議中直接提供屬性和方法的默認實現,但是我們可以通過協議擴展來達到此目的。
protocol MyProtocol {
var prop: Int { get set }
var propertyWithImplementation: String { get }
func foo()
}
extension MyProtocol {
var propertyWithImplementation: String {
return "foo"
}
func foo() {
print(prop)
}
}
class MyClass: MyProtocol {
var prop: Int = 29
}
Kotlin:
哇~,你們這個協議擴展有點厲害了。
Swift:
是的,正是這個特性,才使得我們面向協議編程成為可能。我們甚至可以在擴展中添加協議里沒有定義過的方法和屬性。
extension MyProtocol {
func isExceed() -> Bool {
return prop > 30
}
}
let instance = MyClass()
print(instance.isExceed())
// false
Kotlin:
這就意味著你們也有能力擴展標準庫里的協議了,可以很方便的給標準庫里的協議添加新的方法和屬性。
Swift:
聰明,確實是這樣。不僅如此,在擴展協議的時候,還可以指定一些限制條件,只有遵循協議的類型滿足這些限制條件時,才能獲得協議擴展提供的默認實現。
protocol TextRepresentable {
var textualDeion: String { get }
}
struct Hamster: TextRepresentable {
var name: String
var textualDeion: String {
return "A hamster named (name)"
}
}
extension Collection where Iterator.Element: TextRepresentable {
var textualDeion: String {
let itemsAsText = self.map { $0.textualDeion }
return "[" + itemsAsText.joined(separator: ", ") + "]"
}
}
let hamsters = [Hamster(name: "Jim"), Hamster(name: "Merry")]
print(hamsters.textualDeion)
// [A hamster named Jim, A hamster named Merry]
這里擴展了 Swift 標準庫中的 Collection 協議,但是限制只適用于集合中的元素遵循了 TextRepresentable 協議的情況。 因為 Swift 中 Array 符合 Collection 協議,而 Hamster 類型又符合 TextRepresentable 協議,所以 hamsters 可以使用 textualDeion 屬性得到數組內容的文本表示。
Kotlin:
贊啊~,你們這個協議擴展太強大了,不僅可以擴展自己定義的協議,還可以擴展標準庫中的協議,怪不得蘋果稱 Swift 是面向協議編程的語言。
Swift 在實現多個協議時,會不會有不同協議帶來同名方法或屬性的沖突的問題?
Swift:
我們還不能很好地處理多個協議的沖突問題。
Kotlin:
Kotlin 可以,Kotlin 有一套規則來處理這樣的沖突。在 Kotlin 中,如果一個類從它的直接超類繼承相同成員的多個實現(由于接口函數可以有實現),它必須覆蓋這個成員并提供其自己的實現。 為了表示采用從哪個超類型繼承的實現,我們使用由尖括號中超類型名限定的 super,如 super。
open class A {
open fun f() { print("A") }
fun a() { print("a") }
}
interface B {
fun f() { print("B") } // interface members are 'open' by default
fun b() { print("b") }
}
class C() : A(), B {
// The compiler requires f() to be overridden:
override fun f() {
super.f() // call to A.f()
.f() // call to B.f()
super
}
}
Swift:
這個好贊,可以不怕名字沖突了 。
Kotlin 的接口中可以聲明類方法嗎?
Kotlin:
Kotlin 里面已經沒有類方法的概念了。
Swift:
我們可以在協議中使用 static 關鍵字來聲明類型方法,如果實現該協議的類型是 class 類型,則在實現類中除了用 static 來修飾類型方法外,也可以使用 class關鍵字.
protocol SomeProtocol {
static func someTypeMethod()
}
class SomeClass: SomeProtocol {
// 這里也可以用 static 修飾,區別是 static 修飾的屬性
// 或方法不能被子類復寫,class 修飾的可以被子類復寫
class func someTypeMethod() {
print("type method")
}
}
Kotlin:
我們的接口雖然不支持類方法,但是我們可以給接口中定義的方法的參數設置默認值。
Swift:
這。。。我們不支持為協議中的方法的參數提供默認值。
Kotlin:
方法參數的默認值必須定義在接口中,在實現類或對象實現該方法時,不能為函數提供默認值。同時接口的中函數不能用 JVMOverride 注解修飾,所以接口中定義的帶有默認值的參數,不能為 Java 生成重載方法,如果接口是定義在庫里面,Kotlin 的實現也無法使用自動重載功能,需要手動重載。
interface IDownload{
fun(url: String, isSupportBreakpointC: Boolean = true)
}
class DownloadImpl: IDownload{
override fun(url: String, isSupportBreakpointC: Boolean){
}
}
Swift:
這點算你強。
我們的協議中可以定義可變方法,如果協議中定義的實例方法會改變遵循該協議的類型的實例,那么需要在該方法前加 mutating 關鍵字, 表示可以在該方法中修改它所屬的實例以及實例的任意屬性的值, 例如:
protocol Togglable {
mutating func toggle()
}
enum OnOffSwitch: Togglable {
case off, on
mutating func toggle() {
switch self {
case .off:
self = .on
case .on:
self = .off
}
}
}
var lightSwitch = OnOffSwitch.off
lightSwitch.toggle()
// lightSwitch 現在的值為 .On
Kotlin:
我們沒這特性,這點你贏了。
Swift:
豈止如此,我們的協議中還可以要求遵循協議的類型實現指定的構造器:
protocol SomeProtocol {
init(someParameter: Int)
}
class SomeClass: SomeProtocol {
required init(someParameter: Int) {
// initializer implementation goes here
}
}
在符合協議的類中實現構造器,必須在構造器實現前加上 required 修飾符。使用 required 修飾符可以確保所有子類也必須提供此構造器實現,從而也能符合協議。 如果類已經被標記為 final,那么不需要在協議構造器的實現中使用 required 修飾符,因為 final 類不能有子類.
協議還可以為遵循協議的類型定義可失敗構造器。
Kotlin:
好吧,我們不可以在接口中聲明構造器。
Swift:
你們的接口可以繼承嗎?
Swift 中協議能夠繼承一個或多個其他協議,可以在繼承的協議的基礎上增加新的要求.
Kotlin:
當然可以,這是基本的用法好伐。
Swift:
好吧。。我們還可以通過讓協議繼承 AnyObject 協議來限制協議只能被 Class 類型遵循,而結構體或枚舉不能遵循該協議。
Kotlin:
我們并沒有這種限制,接口可以被類和枚舉實現。
Swift:
你們的接口可以組合嗎?
Swift 可以采用 & 符號將多個協議進行組合:
protocol Named {
var name: String { get }
}
protocol Aged {
var age: Int { get }
}
struct Person: Named, Aged {
var name: String
var age: Int
}
func wishHappyBirthday(to celebrator: Named & Aged) {
print("Happy birthday, (celebrator.name), you're (celebrator.age)!")
}
let birthdayPerson = Person(name: "Malcolm", age: 21)
wishHappyBirthday(to: birthdayPerson)
// Prints "Happy birthday, Malcolm, you're 21!"
這里 wishHappyBirthday(to:) 函數的參數類型為 Named & Aged, 這意味著它不關心參數的具體類型,只要參數符合這兩個協議即可。當然也可以給組合的協議指定一個別名:typealias Property = Named & Aged
Kotlin:
666,你們的協議真是玩出花了,這個功能我們也沒有??。
Swift:
除了協議與協議組合外,協議還可以與類進行組合:
class Location {
var latitude: Double
var longitude: Double
init(latitude: Double, longitude: Double) {
self.latitude = latitude
self.longitude = longitude
}
}
class City: Location, Named {
var name: String
init(name: String, latitude: Double, longitude: Double) {
self.name = name
super.init(latitude: latitude, longitude: longitude)
}
}
func beginConcert(in location: Location & Named) {
print("Hello, (location.name)!")
}
let seattle = City(name: "Seattle", latitude: 47.6, longitude: -122.3)
beginConcert(in: seattle)
這里的 beginConcert(in:) 函數的參數要求是 Location 的子類,且必須符合 Named 協議.
Kotlin:
太贊了~,給你點32個贊!
Swift:
你們是怎么判斷某個實例是否符合某個協議的?
Kotlin:
這就是判斷某個對象是否是某個類型嘛,當然是用 is 啦,如果是類型轉換的話用 as 。
Swift:
嗯嗯,這點我們是一致的。
你們可以定義接口中的方法或屬性為可選嗎?
Kotlin:
何謂可選?
Swift:
就是可以實現也可以不實現
Kotlin:
前面講過了啊,如果接口中的屬性或方法在實現類中可以實現也可以不實現,則可以在接口定義中為該方法提供默認實現。
Swift:
嗯,Swift 是通過協議擴展提供默認實現來到達可選的目的。
不過 Swift 也可以像 Objective-C 里那樣定義可選的接口方法,就需要在 protocol 定義之前加上 @objc,將 protocol 變為 Objective-C 的。然后使用 optional 關鍵字來聲明某些方法或屬性在符合該協議的類中可以不實現,如下:
@objc protocol CounterDataSource {
@objc optional func incrementForCount(count: Int) -> Int
@objc optional var fixedIncrement: Int { get }
}
需要注意的是,標記為 @objc 的 protocol 只能被 class 實現,不能被 struct 和 enum 類型實現,而且實現它的 class 中的方法也必須被標注為 @objc,或者整個類就是繼承自 NSObject。
Kotlin:
額。。。這豈不是很蛋疼
Swift:
是的,所以這種方式并不提倡。
Swift 可以使用 Protocol 來實現委托(代理)模式,委托(代理)模式允許類或結構體將一些需要它們負責的功能委托給其他類型的實例,如下:
protocol RentHouserDelegate{
func rent(_ name:String);
}
class Tenant {
var name = "lucy"
var delegate: RentHouserDelegate?
func rentHouse(){
delegate?.rent(name)
}
}
class Intermediary: RentHouserDelegate {
var name="lily"
func rent(_ name:String) {
print("(name) 請 (self.name) 幫她租一套房子");
}
}
var person = Tenant();
person.delegate = Intermediary()
person.rentHouse()
// lucy 請 lily 幫她租一套房子
Kotlin:
這是接口的一種常用方法,我們依賴注入框架就大量使用這種方式。
Swift:
哈哈,英雄所見略同。
好了,就到這吧,今天的PK互有攻防,好帶勁??~
Kotlin:
總體來說還是你們的協議比較強大。
Swift:
那是,要不然蘋果怎么敢稱 Swift 是一門面向協議編程的語言呢
Kotlin:
好吧,咱們來日方長。
Swift:
嗯嗯,后會有期。
知識點總結
Kotlin
接口定義
同 Java 一樣,Kotlin 用 interface 關鍵字來定義接口,Kotlin 接口中可以有函數的實現,也可以只有抽象方法,接口無法保存狀態,它可以有屬性但必須聲明為抽象或提供訪問器實現。
interface MyInterface {
fun bar()
fun foo() {
// 可選的方法體
}
}
實現接口
Kotlin 的一個類或者對象可以實現一個或多個接口。由于 Kotlin 接口本身的函數式可以有實現的,所以在一個類或對象實現多個接口的時候,就有可能發生沖突,這包括接口之間的的成員沖突,也包括接口與父類直接的成員沖突。
覆蓋沖突
在 Kotlin 中,如果一個類從它的直接超類繼承相同成員的多個實現(由于接口函數可以有實現),它必須覆蓋這個成員并提供其自己的實現。 為了表示采用從哪個超類型繼承的實現,我們使用由尖括號中超類型名限定的 super,如 super
。
open class A {
open fun f() { print("A") }
fun a() { print("a") }
}
interface B {
fun f() { print("B") } // interface members are 'open' by default
fun b() { print("b") }
}
class C() : A(), B {
// The compiler requires f() to be overridden:
override fun f() {
super.f() // call to A.f()
super.f() // call to B.f()
}
}
同時繼承 A 和 B 沒問題,并且 a() 和 b() 也沒問題因為 C 只繼承了每個函數的一個實現。 但是 f() 由 C 繼承了兩個實現,所以我們必須在 C 中覆蓋 f() 并且提供我們自己的實現來消除歧義。
接口中的屬性
Kotlin 中可以在接口中定義屬性。在接口中聲明的屬性要么是抽象的,要么提供訪問器的實現。在接口中聲明的屬性不能有幕后字段(backing field),因此接口中聲明的訪問器不能引用它們。
interface MyInterface {
val prop: Int // 抽象的
val propertyWithImplementation: String
get() = "foo"
fun foo() {
print(prop)
}
}
class Child : MyInterface {
override val prop: Int = 29
}
函數默認參數與重載
如果接口函數需要定義默認值的話,必須定義在接口中,在實現類或對象實現該方法時,不能為函數提供默認值。同時接口的中函數不能用 JVMOverride 注解修飾,所以接口中定義的帶有默認值的參數,不能為 Java 生成重載方法,如果接口是定義在庫里面,Kotlin 的實現也無法使用自動重載功能,需要手動重載。
interface IDownload{
fun(url: String, isSupportBreakpointC: Boolean = true)
}
class DownloadImpl{
override fun(url: String, isSupportBreakpointC: Boolean){
}
}
Swift
Protocol
Swift 是一門支持面向協議編程的語言,在 Swift 語言中,協議被賦予了更多的功能和更廣闊的使用空間。恰逢蘋果發布了 swift 4,以下都是基于最新的 swift 4 語法進行講述。
協議語法
協議聲明:
protocol SomeProtocol {
// protocol definition goes here
}
要讓自定義類型符合某個協議,需要在類型名稱后加上協議名稱,中間以冒號(:)分隔。符合多個協議時,各協議之間用逗號(,)分隔. swift 中,符合協議的類型可以是 class、struct 或 enum。
struct SomeStructure: FirstProtocol, AnotherProtocol {
// structure definition goes here
}
需要注意的是,如果某個類在符合某個協議的同時又繼承自某個父類,應將其父類名放在其符合的協議名之前。
協議屬性聲明
協議中可以聲明符合此協議的類型必須實現的屬性:
protocol SomeProtocol {
var mustBeSettable: Int { get set }
var doesNotNeedToBeSettable: Int { get }
}
協議不指定屬性是存儲型屬性還是計算型屬性,它只指定屬性的名稱和類型,以及屬性是可讀的還是可讀可寫的。
協議總是用 var 關鍵字來聲明變量屬性,在類型聲明后加上 { set get } 來表示屬性是可讀可寫的,用 { get } 來表示屬性是只讀的。
協議中總是使用 static 關鍵字定義類型屬性,如果是 class類型實現協議,除了 static,還可以使用 class 關鍵字來聲明類型屬性。
protocol AnotherProtocol {
static var someTypeProperty: Int { get }
}
class SomeClass: AnotherProtocol {
// 這里也可以用 static 修飾,區別是 static 修飾的屬性
或方法不能被子類復寫,class 修飾的可以被子類復寫
class var someTypeProperty: Int {
return 0
}
}
協議方法聲明
協議可以要求遵循協議的類型實現某些指定的實例方法或類方法。需要注意的是,不支持為協議中的方法的參數提供默認值。
protocol RandomNumberGenerator {
func random() -> Double
}
與屬性類似,在協議中也使用 static 定義類方法,當 class 類型實現協議時,可以使用 class 關鍵字來修飾.
如果協議中定義的實例方法會改變遵循該協議的類型的實例,那么需要在該方法前加 mutating 關鍵字, 表示可以在該方法中修改它所屬的實例以及實例的任意屬性的值, 例如:
protocol Togglable {
mutating func toggle()
}
enum OnOffSwitch: Togglable {
case off, on
mutating func toggle() {
switch self {
case .off:
self = .on
case .on:
self = .off
}
}
}
var lightSwitch = OnOffSwitch.off
lightSwitch.toggle()
// lightSwitch 現在的值為 .On
協議構造器聲明
協議可以要求遵循協議的類型實現指定的構造器:
protocol SomeProtocol {
init(someParameter: Int)
}
class SomeClass: SomeProtocol {
required init(someParameter: Int) {
// initializer implementation goes here
}
}
在符合協議的類中實現構造器,必須在構造器實現前加上 required 修飾符。使用 required 修飾符可以確保所有子類也必須提供此構造器實現,從而也能符合協議. 如果類已經被標記為 final,那么不需要在協議構造器的實現中使用 required 修飾符,因為 final 類不能有子類.
協議還可以為遵循協議的類型定義可失敗構造器。
委托(代理)模式
Swift 可以使用 Protocol 來實現委托(代理)模式,委托(代理)模式允許類或結構體將一些需要它們負責的功能委托給其他類型的實例,如下:
protocol RentHouserDelegate{
func rent(_ name:String);
}
class Tenant {
var name = "lucy"
var delegate: RentHouserDelegate?
func rentHouse(){
delegate?.rent(name)
}
}
class Intermediary: RentHouserDelegate {
var name="lily"
func rent(_ name:String) {
print("(name) 請 (self.name) 幫她租一套房子");
}
}
var person = Tenant();
person.delegate = Intermediary()
person.rentHouse()
// lucy 請 lily 幫她租一套房子
通過擴展遵循協議
可以通過擴展令已有類型遵循并符合協議:
protocol TextRepresentable {
var textualDeion: String { get }
}
struct Circular {
var radius: Int
}
extension Circular: TextRepresentable {
var textualDeion: String {
return "The circular's radius is (radius)"
}
}
let circular = Circular(radius: 2)
print(circular.textualDeion)
// The circular's radius is 2
當一個類型已經符合了某個協議中的所有要求,卻還沒有聲明遵循該協議時,可以通過空擴展來使該類型遵循該協議:
struct Square {
var width: Int
var textualDeion: String {
return "The square's width is (width)"
}
}
extension Square: TextRepresentable {}
let square = Square(width: 3)
let squareTextRepresentable: TextRepresentable = square
print(squareTextRepresentable.textualDeion)
// The square's width is 3
協議類型
盡管協議本身并未實現任何功能,但是協議可以被當做一個成熟的類型來使用。協議類型也可以在數組或者字典這樣的集合中使用:
let things: [TextRepresentable] = [circular, square]
for thing in things {
print(thing.textualDeion)
}
協議的繼承
協議能夠繼承一個或多個其他協議,可以在繼承的協議的基礎上增加新的要求:
protocol PrettyTextRepresentable: TextRepresentable {
var prettyTextualDeion: String { get }
}
extension Square: PrettyTextRepresentable {
var prettyTextualDeion: String {
var output = textualDeion + ": "
output += "the area is (width*width)"
return output
}
}
print(square.prettyTextualDeion)
// The square's width is 3: the area is 9
Class 類型專屬協議
通過讓協議繼承 AnyObject 協議來限制協議只能被 Class 類型遵循,而結構體或枚舉不能遵循該協議
protocol SomeClassOnlyProtocol: AnyObject, SomeInheritedProtocol {
// class-only protocol definition goes here
}
協議合成
可以采用 & 符號將多個協議進行組合:
protocol Named {
var name: String { get }
}
protocol Aged {
var age: Int { get }
}
struct Person: Named, Aged {
var name: String
var age: Int
}
func wishHappyBirthday(to celebrator: Named & Aged) {
print("Happy birthday, (celebrator.name), you're (celebrator.age)!")
}
let birthdayPerson = Person(name: "Malcolm", age: 21)
wishHappyBirthday(to: birthdayPerson)
// Prints "Happy birthday, Malcolm, you're 21!"
這里 wishHappyBirthday(to:) 函數的參數類型為 Named & Aged, 這意味著它不關心參數的具體類型,只要參數符合這兩個協議即可。當然也可以給組合的協議指定一個別名:typealias Property = Named & Aged
除了協議與協議組合外,協議還可以與 class 進行組合:
class Location {
var latitude: Double
var longitude: Double
init(latitude: Double, longitude: Double) {
self.latitude = latitude
self.longitude = longitude
}
}
class City: Location, Named {
var name: String
init(name: String, latitude: Double, longitude: Double) {
self.name = name
super.init(latitude: latitude, longitude: longitude)
}
}
func beginConcert(in location: Location & Named) {
print("Hello, (location.name)!")
}
let seattle = City(name: "Seattle", latitude: 47.6, longitude: -122.3)
beginConcert(in: seattle)
這里的 beginConcert(in:) 函數的參數要求是 Location 的子類,且必須符合 Named 協議.
檢查協議一致性
可以通過 is as? as 來檢查某個實例是否符合某個協議:
let things: [Any] = [circular, square, "abc"]
for thing in things {
if let object = thing as? TextRepresentable {
print(object.textualDeion)
} else {
print("It does not conform to TextRepresentable")
}
}
可選協議
原生的 Swift protocol 里沒有可選項,所有定義的方法都是必須實現的。如果想要像 Objective-C 里那樣定義可選的接口方法,就需要在 protocol 定義之前加上 @objc,將 protocol 變為 Objective-C 的。然后使用 optional 關鍵字來聲明某些方法或屬性在符合該協議的類中可以不實現,如下:
@objc protocol CounterDataSource {
@objc optional func incrementForCount(count: Int) -> Int
@objc optional var fixedIncrement: Int { get }
}
需要注意的是,標記為 @objc 的 protocol 只能被 class 實現,不能被 struct 和 enum 類型實現,而且實現它的 class 中的方法還必須也被標注為 @objc,或者整個類就是繼承自 NSObject。這還是很蛋疼的。
協議擴展
協議可以通過擴展來為遵循協議的類型提供屬性和方法的實現,即使協議中沒有聲明。這樣就無需在每個遵循協議的類型中都重復同樣的實現,也無需使用全局函數。
protocol TextRepresentable {
var textualDeion: String { get }
}
extension TextRepresentable {
func hasDeion() -> Bool {
return !textualDeion.isEmpty
}
}
還可以通過協議擴展來為協議要求的屬性、方法提供默認的實現。這樣在遵循這個協議的類型中,可以不用實現這個屬性或方法,調用的時候默認調 extension 中的實現。這也相當于變相將 protocol 中的屬性或方法設定為了 `optional.
extension TextRepresentable {
var textualDeion: String {
return "This is a shape"
}
}
在擴展協議的時候,也可以指定一些限制條件,只有遵循協議的類型滿足這些限制條件時,才能獲得協議擴展提供的默認實現。
extension Collection where Iterator.Element: TextRepresentable {
var textualDeion: String {
let itemsAsText = self.map { $0.textualDeion }
return "[" + itemsAsText.joined(separator: ", ") + "]"
}
}
let circulars = [Circular(radius: 1), Circular(radius: 2)]
print(circulars.textualDeion)
// [The circular's radius is 1, The circular's radius is 2]
這里擴展了 Collection 協議,但是限制只適用于集合中的元素遵循了 TextRepresentable 協議的情況。 因為 Swift 中 Array 符合 Collection 協議,而 Circular 類型又符合 TextRepresentable 協議,所以 circulars 可以使用 textualDeion 屬性得到數組內容的文本表示
文章轉載自網絡
|
新聞熱點
疑難解答