2017-01-13 10 views
5

Я пытаюсь понять, почему, когда раздел общего метода игнорируетсяНеправильная Обобщенная функция перегрузки называется

Я сделал простой случай использования в 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 протокол для работы, нам необходимо пройти сильный тип, который соответствует протоколу в его определение.

Просто использование супертипов недостаточно, даже если в тесте № 3 очевидно, что данный экземпляр фактически соответствует протоколу HasChildren.

Итак, что мне здесь не хватает, это просто невозможно? Есть ли у вас ссылки, содержащие больше информации о том, что происходит, или больше информации о предложениях where, или подтипировании и его поведении в целом?

Я прочитал несколько полезных ресурсов, но ни один не кажется, исчерпывающее объяснение о том, почему он не работает:

ответ

3

Тип метод, который будет вызываться, выбирается во время компиляции. Что компилятор знает о ваших типах?

if let seniorFirstChildren = senior.children.first { 

seniorFirstChildren является Human, потому что это как children объявлены. У нас нет информации о том, является ли child взрослым или старшим.

Однако, считают это:

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

Теперь компилятор знает seniorFirstChildren является AdultHuman и он будет вызывать метод, который вы ожидаете.

Вы должны различать статических типов (тип, известные во время компиляции) и динамических типов (типов, известных во время выполнения).

+0

Ну, это довольно очевидно, когда вы так говорите. Не могу поверить, что я пропустил это ... Думаю, моя идея рекурсивных общих функций не будет работать должным образом, если мы не сможем сделать их динамичными. –

3

От the Language Guide - Type Casting:

Проверка Тип

Используйте контрольный тип оператора (is), чтобы проверить, является ли экземпляр имеет определенного типа подкласса.Оператор проверки типа возвращает true, если экземпляр относится к этому типу подкласса и false, если это не так.

Проверка типа оператор is разрешен в выполнении, в то время как разрешение перегрузки вызова sayHelloToFamily с использованием первого children члена экземпляра AdultHuman (переплетено с seniorFirstChildren) в качестве аргумента разрешена в время компиляции (в этом случае он напечатан как Human, который не соответствует HasChildren). Если вы явно сообщите компилятору, что seniorFirstChildren является экземпляром AdultHuman (с использованием небезопасного as! AdultHuman), то, естественно, компилятор будет использовать эту информацию для выбора более конкретной перегрузки.