结果是“ a”,但我希望它是“ b”。我想知道为什么以及如何在不带参数的情况下调用doTest
来打印“ b”。
class AA {
func doTest() {
print("a")
}
}
class BB: AA {
func doTest(_ different: Bool = true) {
print("b")
}
}
let bObjc = BB()
bObjc.doTest()
答案 0 :(得分:11)
类BB
不会覆盖AA
中的方法,这意味着BB
上存在两个方法:
func doTest() // inherited
func doTest(_ different: Bool = true) // declared
致电时
bObjc.doTest()
编译器必须选择其中之一。并且它比没有参数的方法更喜欢没有参数的方法。
答案 1 :(得分:9)
得到a
输出的原因是因为您没有调用期望func doTest(_ different: Bool = true)
的新方法Bool
。
bObjc.doTest(true) // b
您实际上正在做的是调用从父类doTest()
继承的方法AA
。
如果要覆盖AA
中的方法,则只需执行以下操作:
class BB: AA {
override func doTest() {
print("b")
}
}
let bObjc = BB()
bObjc.doTest()
答案 2 :(得分:3)
这是一个不幸的情况。没有办法影响编译器如何用普通的Swift消除调用的歧义。
让我们看看一些选项:
BB.doTest()
以将调用转发给BB.doTest(_:)
#selector(BB.doTest(_:))
指定所需方法的签名在这种情况下,强制编译器不使用最简单的方法签名的最简单方法是覆盖doTest
中的B
并重新路由调用:
class AA {
func doTest() {
print("a")
}
}
class BB: AA {
override func doTest() {
self.doTest(true)
}
func doTest(_ different: Bool = true) {
print("b")
}
}
let bObjc = BB()
bObjc.doTest()
如果那不可能,请继续阅读。
doTest(_:)
选择器(ObjC)您可以指定是否要通过选择器调用doTest()
或doTest(_:)
。但是,只有将方法注释为@objc
并使类型从NSObject
继承时,才可以由其选择器调用方法。因此,在实践中这可能会过分杀人。
import Foundation
class AA: NSObject {
@objc func doTest() {
print("a")
}
}
class BB: AA {
@objc func doTest(_ different: Bool = true) {
print("b")
}
}
let bObjc = BB()
bObjc.performSelector(onMainThread: #selector(BB.doTest(_:)), with: nil, waitUntilDone: true)
通过拆分进一步定义doTest()
和doTest(_:)
的类型,可以帮助编译器选择正确的解决方案。使用协议,然后将doTest
消息的接收方强制转换为协议,以使编译器知道应使用该协议中定义的方法。
协议本身:
protocol DoTestSpecific {
func doTest(_ different: Bool)
}
不幸的是,您不能在协议的方法 definitions 中指定默认参数值。只有实现可以定义默认参数。
BB
类中的协议投射(bObjc as DoTestSpecific)
将告诉编译器完全不考虑AA.doTest
。由于该协议未指定默认参数,因此您现在必须在呼叫站点上提供该值:
class AA {
func doTest() {
print("a")
}
}
class BB: AA, DoTestSpecific {
func doTest(_ different: Bool = true) {
print("b")
}
}
let bObjc = BB()
(bObjc as DoTestSpecific).doTest(false)
强制转换(bObjc as DoTestSpecific)
的结果不知道BB
的方法实现以及该方法附带的默认参数值。
但是您可以将实现转移到协议扩展,从而使不需要参数的实现甚至对于转换结果来说都是已知的!
最终代码:
class AA {
func doTest() {
print("a")
}
}
protocol DoTestSpecific {
func doTest(_ different: Bool)
}
extension DoTestSpecific {
func doTest(_ different: Bool = true) {
print("b")
}
}
class BB: AA, DoTestSpecific {
}
let bObjc = BB()
(bObjc as DoTestSpecific).doTest()
这按预期工作。它要求添加协议和协议扩展中的实现。但是现在呼叫站点上已经不再有歧义了。
如果示例代码更加复杂,并且包含对对象状态或其他对象的依赖关系,则可能很难甚至无法实现。