我试图理解为什么忽略泛型方法的where子句
我在Swift 3中创建了一个简单的用例(如果你想操作它,可以在操场上复制代码):
//MARK: - Classes
protocol HasChildren {
var children:[Human] {get}
}
class Human {}
class SeniorHuman : Human, HasChildren {
var children: [Human] {
return [AdultHuman(), AdultHuman()]
}
}
class AdultHuman : Human, HasChildren {
var children: [Human] {
return [YoungHuman(), YoungHuman(), YoungHuman()]
}
}
class YoungHuman : Human {}
//MARK: - Generic Methods
/// This method should only be called for YoungHuman
func sayHelloToFamily<T: Human>(of human:T) {
print("Hello \(human). You have no children. But do you conform to protocol? \(human is HasChildren)")
}
/// This method should be called for SeniorHuman and AdultHuman, but not for YoungHuman...
func sayHelloToFamily<T: Human>(of human:T) where T: HasChildren {
print("Hello \(human). You have \(human.children.count) children, good for you!")
}
好的,现在让我们进行一些测试。如果我们有:
let senior = SeniorHuman()
let adult = AdultHuman()
print("Test #1")
sayHelloToFamily(of: senior)
print("Test #2")
sayHelloToFamily(of: adult)
if let seniorFirstChildren = senior.children.first {
print("Test #3")
sayHelloToFamily(of: seniorFirstChildren)
print("Test #4")
sayHelloToFamily(of: seniorFirstChildren as! AdultHuman)
}
输出结果为:
Test #1
Hello SeniorHuman. You have 2 children, good for you!
Test #2
Hello AdultHuman. You have 3 children, good for you!
Test #3
Hello AdultHuman. You have no children. But do you conform to protocol? true
//Well, why are you not calling the other method then?
Test #4
Hello AdultHuman. You have 3 children, good for you!
//Oh... it's working here... It seems that I just can't use supertyping
嗯......显然,要使where
协议子句起作用,我们需要传递一个符合其定义中的协议的强类型。
仅仅使用supertyping是不够的,即使在测试#3中显而易见的是,给定实例实际上符合HasChildren
协议。
所以,我在这里错过了什么,这是不可能的?你是否有一些链接提供有关正在发生的事情的更多信息,或有关where
条款或子类型的更多信息和它的一般行为?
我已经阅读了一些有用的资源,但似乎没有一个详尽的解释为什么它不起作用:
答案 0 :(得分:3)
在编译期间选择要调用的方法的类型。编译器对您的类型有何了解?
if let seniorFirstChildren = senior.children.first {
seniorFirstChildren
是Human
,因为children
是如何宣布的。我们没有关于child
是成人还是高级的信息。
但是,请考虑一下:
if let seniorFirstChildren = senior.children.first as? AdultHuman {
现在编译器知道seniorFirstChildren
是AdultHuman
,它会调用你期望的方法。
您必须区分静态类型(编译期间已知的类型)和动态类型(运行时已知的类型)。
答案 1 :(得分:3)
来自the Language Guide - Type Casting:
检查类型
使用类型检查运算符(
store = pd.HDFStore(filename) # it's stored in the `'table'` mode per default ! store.append('key_name', df, data_columns=True) ... store.close() # don't forget to flush changes to disk !!!
)来检查实例是否属于 某个子类类型。如果是,则类型检查运算符返回is
instance属于该子类类型,true
如果不是。
类型检查运算符false
在运行时解析,而使用您的实例的第一个is
成员调用sayHelloToFamily
的重载解析children
(绑定到AdultHuman
)因为参数在编译时解析(在这种情况下,它被键入为seniorFirstChildren
,不符合Human
})。如果您明确告诉编译器HasChildren
是seniorFirstChildren
实例(使用不安全的AdultHuman
),那么编译器自然会利用这些信息来选择更具体的重载。