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

Wrong generic overload function called

我想了解为什么忽略泛型方法的 where 子句

我在 Swift 3 中做了一个简单的用例(如果你想 fiddle 使用它,你可以在操场上复制代码):

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

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

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

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

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

if let seniorFirstChildren = senior.children.first {

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

但是,考虑一下:

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

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

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

来自 the Language Guide - Type Casting:

Checking Type

Use the type check operator (is) to check whether an instance is of a certain subclass type. The type check operator returns true if the instance is of that subclass type and false if it is not.

类型检查运算符 is 运行时解析 ,而使用第一个 children 调用 sayHelloToFamily 的重载解析AdultHuman 实例的成员(绑定到 seniorFirstChildren),因为参数在 编译时 解析(在这种情况下,它被键入为 Human,这不符合 HasChildren)。如果您明确告诉编译器 seniorFirstChildren 是一个 AdultHuman 实例(使用不安全的 as! AdultHuman),那么编译器自然会利用此信息来选择更具体的重载。