[英]Swift Protocol-Oriented Programming: Can Protocol Extension Property Have Same Name As Base Class Property
[英]Swift: Protocol and Base Class with same function name
所以我只是在玩协议,只是对以下行为感到困惑。 我有一个与 function 和一个基本 class 具有相同 function 名称的协议。 现在,另一个 class 从基础 class 继承并实现协议。 但在某些情况下它不会产生错误。
例如:
protocol P {
func greetings()
}
class Base {
func greetings() {
print("Hello Base")
}
}
class A: Base {
override func greetings() {
print("Hello Class")
}
}
extension A: P {
}
let a = A()
print(a.greetings()) // print: Hello Class
let b:Base = A()
print(b.greetings()) // print: Hello Class
let p:P = A()
print(p.greetings()) // print: Hello Class
那么,class 与协议的一致性如何?
同样在以下情况下,当协议 function 具有默认实现时,它编译时不会出现任何错误,并且始终选择基础 class function。
protocol P {
func greetings()
}
extension P {
func greetings() {
print("Hello Protocol")
}
}
class Base {
func greetings() {
print("Hello Base")
}
}
class A: Base {
}
extension A: P {
}
let a = A()
print(a.greetings()) // print: Hello Base
let b:Base = A()
print(b.greetings()) // print: Hello Base
let p:P = A()
print(p.greetings()) // print: Hello Base
为什么它总是选择基础 class function? 为什么没有编译时错误,因为 function 调用不明确?
您将 object 分配给的变量类型不控制如何在该 object 上调度函数。 在查找 function 时,计算机从 object 开始,然后“向上”遍历 class 层次结构以寻找实现。 IE。 它从A
开始,如果找不到 function,它会上升到Base
( A
的超类)并在那里查找。 如果仍然没有找到,它会上升到下一个超类(在这种情况下没有超类)。 如果未找到 function,则会出现错误。
在您的第一个示例中,您有一个A
的实例,并且A
覆盖了基本greetings
,因此将始终使用该实现。 这与 Object Oriented Programming -Substitutability中的一个重要概念有关。
在这种情况下,您可以声明一个Base
类型的变量,但分配一个更专业的 class - A
的实例,但程序仍然可以正常运行。
请记住,您的第一个作业( let a = A()
)中所示的类型推断并非在所有语言中都存在。 在许多情况下,您需要显式声明变量的类型。 如果声明Base
类型的变量意味着调用Base
function 而不是子类,那么它将破坏子类化的目的。
关于你的问题
class 与协议的一致性如何?
协议是一种规范,但不是一种实现(接下来我们将看看您关于默认实现的问题)。 你的协议说所有符合P
的东西都必须提供一个greeting
function。 显然Base
和A
都提供了问候 function。 您的扩展仅添加了A
符合P
的规定。 它不需要添加P
的实现,因为greeting
方法已经存在。
如果您删除了extension A: P
那么let p:P = A()
将给出错误,因为编译器不再知道A
符合P
。
这使我们想到了您的最后两个问题:
为什么它总是选择基础 class function? 为什么没有编译时错误,因为 function 调用不明确?
您可以使用协议扩展为该协议的任何方法或计算属性要求提供默认实现。 如果符合类型提供了它自己的所需方法或属性的实现,则将使用该实现而不是扩展提供的实现。
在您的第二个示例块中, A
不会覆盖greeting
的Base
实现,因此始终调用Base
实现。
您添加到P
的greeting
的默认实现仅在符合P
的 class 不提供实现的情况下使用。
默认实现可以通过提供适用于所有或大多数情况的某些行为的实现来更容易地遵守协议。 如果符合的 class 提供实现,则始终忽略默认实现
考虑以下示例:
Protocol Vehicle {
func soundHorn()
var numberOfDoors: Int
}
extension Vehicle {
func soundHorn() {
print("Beep")
}
}
class SportsCar: Vehicle {
var numberOfDoors: Int {
return 2
}
}
class Sedan: Vehicle {
var numberOfDoors: Int {
return 4
}
}
class Truck: Vehicle {
var numberOfDoors: Int {
return 2
}
func soundHorn() {
print("**HONK**")
}
}
在这里,我们定义了一个简单的协议Vehicle
,它表示Vehicle
有许多门并且可以按喇叭。 我们提供了一个打印“Beep”的默认soundHorn
。 然后我们声明了实现这个协议的类。 这两种不同类型的汽车有不同数量的车门,但我们对默认喇叭感到满意。 对于Truck
,我们有一个更大、更响亮的喇叭,所以我们实现soundHorn
而不是使用默认值:
var v: Vehicle = SportsCar()
v.soundHorn() // Prints "beep"
v = Truck()
v.soundHorn() // Prints "**HONK**"
注意v
是Vehicle
类型的,但我们根据实际分配给v
的 object 得到正确的喇叭。 编译器很高兴,因为它知道所有Vehicles
都可以soundHorn
; 它不需要知道具体的车辆类型。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.