Swift: 协议类和基类具有相同的函数名

问题描述 投票:-2回答:1

所以我只是在玩协议,只是对下面的行为感到困惑.我有一个带有函数的协议和一个具有相同函数名的基类。现在,另一个类从基类继承并实现了一个协议。但在某些情况下,它没有产生错误。

比如说

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

为什么它总是选择基类函数?为什么没有因为模棱两可的函数调用而出现编译时的错误?

ios swift xcode protocols swift-playground
1个回答
1
投票

你分配给一个对象的变量类型并不能控制如何在该对象上调度函数。 当寻找一个函数时,计算机从对象开始,然后在类的层次结构中 "向上 "行走,寻找一个实现。 即:它从以下位置开始 A 如果找不到函数,它就会上升到 Base (A的超类)并在那里查找。 如果仍然没有找到,它就会进入下一个超类(本例中没有超类)。 如果函数没有被找到,那么你会得到一个错误。

在你的第一个例子中,你有一个 AA 覆盖基数 greetings 所以,那就是那个将永远被使用的实现。 这与面向对象编程中的一个重要概念有关----。可替代性.

在这种情况下,你可以声明一个类型为 Base 但却指定了一个更专业的类的实例------。A,但程序仍能正确运行。

请记住,类型推理如你的第一次作业所示(let a = A())并不是在所有的语言中都存在的,在很多情况下,你需要明确的声明一个变量的类型。 在很多情况下,你需要明确地声明一个变量的类型。 如果声明一个类型为 Base 这意味着 Base 函数,而不是调用子类,这就违背了子类的目的。

关于你的问题

类与协议的一致性是如何实现的?

协议是一个规范,但不是一个实现(我们接下来会看你围绕默认实现的问题)。 你的协议说,所有符合 P 必须提供 greeting 功能。 显然,这两个 BaseA 提供问候功能。 你的扩展只是增加了一个规定,即 A 符合 P. 它不需要添加实现的 P 因为 greeting 方法已经存在。

如果你删除了 extension A: P 然后 let p:P = A() 将会给出一个错误,因为编译器不再知道 A 符合 P.

这就涉及到你的最后两个问题。

为什么它总是选择一个基类函数?以及为什么没有因为模棱两可的函数调用而出现编译时错误?

Swift编程语言

你可以使用协议扩展来为该协议的任何方法或计算属性需求提供一个默认实现。如果一个符合协议的类型提供了它自己对所需方法或属性的实现,那么这个实现将被用来代替扩展提供的实现。

在你的第二个示例块中。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它不需要知道具体的车辆类型。

© www.soinside.com 2019 - 2024. All rights reserved.