ios - 调用了错误的通用重载函数

标签 ios swift generics subtyping

我试图理解为什么泛型方法的 where 子句被忽略

我在 Swift 3 中做了一个简单的用例(如果你想摆弄它,你可以在 playground 中复制代码):

//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 协议(protocol)子句起作用,我们需要传递一个符合其定义中的协议(protocol)的强类型。

仅仅使用父类(super class)型是不够的,即使在测试 #3 中很明显给定的实例实际上符合 HasChildren 协议(protocol)。

那么,我在这里遗漏了什么,这是不可能的吗?您是否有一些链接提供有关正在发生的事情的更多信息,或有关 where 子句的更多信息,或子类型及其一般行为?

我已经阅读了一些有用的资源,但似乎都没有关于为什么它不起作用的详尽解释:

最佳答案

要调用的方法的类型是在编译时选择的。编译器对您的类型了解多少?

if let seniorFirstChildren = senior.children.first {

seniorFirstChildrenHuman 因为这就是 children 的声明方式。我们不知道 child 是成人还是老人。

但是,考虑一下:

if let seniorFirstChildren = senior.children.first as? AdultHuman {

现在编译器知道 seniorFirstChildrenAdultHuman 并且它将调用您期望的方法。

您必须区分静态类型(编译期间已知的类型)和动态类型(运行时已知的类型)。

关于ios - 调用了错误的通用重载函数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41632343/

相关文章:

objective-c - 如何从 SQLite 表中检索所有行?

IOS:Facebook 集成 - 共享 UIwebview 的当前 url

swift - Swift 中的 Base-10 float

swift - 更改标签栏颜色(Swift)

ios - 如何在 Swift 中重置 UICollectionView

java - 自动装箱不适用于参数化类型

java - 如何在Java泛型方法中使用int代替T?

ios - OpenGLES 无法在 iOS 的顶点着色器中使用输入参数

iphone - 如何让一个 b2Body 忽略另一个 b2Body?

Java - 类型参数的最大数量