所以我只是在玩协议,只是对下面的行为感到困惑.我有一个带有函数的协议和一个具有相同函数名的基类。现在,另一个类从基类继承并实现了一个协议。但在某些情况下,它没有产生错误。
比如说
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
那么,类与协议的一致性是如何发生的?
另外,在下面的情况下,当协议函数有默认实现时,它编译时没有任何错误,总是选择基类函数。
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
为什么它总是选择基类函数?为什么没有因为模棱两可的函数调用而出现编译时的错误?
你分配给一个对象的变量类型并不能控制如何在该对象上调度函数。 当寻找一个函数时,计算机从对象开始,然后在类的层次结构中 "向上 "行走,寻找一个实现。 即:它从以下位置开始 A
如果找不到函数,它就会上升到 Base
(A
的超类)并在那里查找。 如果仍然没有找到,它就会进入下一个超类(本例中没有超类)。 如果函数没有被找到,那么你会得到一个错误。
在你的第一个例子中,你有一个 A
和 A
覆盖基数 greetings
所以,那就是那个将永远被使用的实现。 这与面向对象编程中的一个重要概念有关----。可替代性.
在这种情况下,你可以声明一个类型为 Base
但却指定了一个更专业的类的实例------。A
,但程序仍能正确运行。
请记住,类型推理如你的第一次作业所示(let a = A()
)并不是在所有的语言中都存在的,在很多情况下,你需要明确的声明一个变量的类型。 在很多情况下,你需要明确地声明一个变量的类型。 如果声明一个类型为 Base
这意味着 Base
函数,而不是调用子类,这就违背了子类的目的。
关于你的问题
类与协议的一致性是如何实现的?
协议是一个规范,但不是一个实现(我们接下来会看你围绕默认实现的问题)。 你的协议说,所有符合 P
必须提供 greeting
功能。 显然,这两个 Base
和 A
提供问候功能。 你的扩展只是增加了一个规定,即 A
符合 P
. 它不需要添加实现的 P
因为 greeting
方法已经存在。
如果你删除了 extension A: P
然后 let p:P = A()
将会给出一个错误,因为编译器不再知道 A
符合 P
.
这就涉及到你的最后两个问题。
为什么它总是选择一个基类函数?以及为什么没有因为模棱两可的函数调用而出现编译时错误?
你可以使用协议扩展来为该协议的任何方法或计算属性需求提供一个默认实现。如果一个符合协议的类型提供了它自己对所需方法或属性的实现,那么这个实现将被用来代替扩展提供的实现。
在你的第二个示例块中。A
不覆盖 Base
实施 greeting
所以 Base
的默认实现总是被调用。
的默认实现。greeting
您添加到 P
只会在类符合 P
不提供实现。
默认实现可以通过提供一些适用于所有或大多数情况的行为的实现,使其更容易符合协议。 如果符合协议的类提供了一个实现,默认实现总是被忽略。
考虑下面的例子。
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
有许多门,可以发出喇叭声。 我们提供了一个默认的 soundHorn
打印 "Beep "的类。 然后我们声明实现这个协议的类。 两种不同类型的汽车的车门数量不同,但我们对默认的喇叭很满意。 对于一个 Truck
我们有一个更大、更响的喇叭,所以我们实现了 soundHorn
而不是使用默认值。
var v: Vehicle = SportsCar()
v.soundHorn() // Prints "beep"
v = Truck()
v.soundHorn() // Prints "**HONK**"
注意 v
属于 Vehicle
但我们会根据实际分配到的对象得到正确的号角。v
. 编译器很高兴,因为它知道所有的 Vehicles
可以 soundHorn
它不需要知道具体的车辆类型。