2017-01-13 19 views
5

Próbuję zrozumieć, dlaczego, gdzie klauzula metody rodzajowej jest ignorowanyBłędna funkcja przeciążenia generic nazywa

Zrobiłem prosty przypadek użycia w Swift 3 (można skopiować kod na placu zabaw jeśli chcesz się z nim bawić):

//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!") 
} 

Ok, więc teraz uruchommy kilka testów. Jeśli mamy:

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) 
} 

Wyjście jest:

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 

Cóż ... widocznie, gdyż klauzula protokół where pracować, musimy zdać silny typ, który jest zgodny z protokołem w swojej definicja.

Samo użycie supertekstu nie wystarczy, nawet jeśli w teście nr 3 oczywiste jest, że dana instancja faktycznie odpowiada protokołowi HasChildren.

Więc, czego tu brakuje, czy to po prostu niemożliwe? Czy masz kilka linków podających więcej informacji o tym, co się dzieje, lub więcej informacji na temat klauzul where, podtypów i ogólnie ich zachowania?

Czytałem kilka przydatnych zasobów, ale nikt zdaje się mieć wyczerpującego wyjaśnienia, dlaczego to nie działa:

Odpowiedz

3

Rodzaj metoda, która ma zostać wywołana, jest wybierana podczas kompilacji. Co kompilator wie o twoich typach?

if let seniorFirstChildren = senior.children.first { 

seniorFirstChildren jest Human bo tak children zostały zadeklarowane. Nie mamy informacji, czy child jest dorosły czy starszy.

Jednak rozważ to:

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

Teraz kompilator wie seniorFirstChildren jest AdultHuman i będzie wywołać metodę można się spodziewać.

Trzeba odróżnić statycznych typów (typy znane podczas kompilacji) i dynamicznych typów (typy znane przy starcie).

+0

Cóż, to dość oczywiste, kiedy mówisz tak. Nie mogę uwierzyć, że tęskniłem za tym ... Myślę, że mój pomysł rekurencyjnych funkcji ogólnych nie działałby wtedy prawidłowo, gdybyśmy nie mogli uczynić ich dynamicznymi. –

3

Od the Language Guide - Type Casting:

Kontrola Rodzaj

Użyj operatora wyboru typu (is), aby sprawdzić, czy instancja jest pewnego typu podklasy.Operator sprawdzania typu zwraca true, jeśli instancja jest tego typu podklasy i jeśli nie jest to false.

Sprawdzenie typu Operator is został rozwiązany w wykonywania, natomiast uchwałą przeciążenia wywołaniu sayHelloToFamily wykorzystaniem pierwszego children członek danej instancji AdultHuman (zbindowanych do seniorFirstChildren) jako argument został rozwiązany w czasie kompilacji (w takim przypadku jest to wpisana jako Human, która nie jest zgodna z HasChildren). Jeśli jawnie powiesz kompilatorowi, że seniorFirstChildren jest instancją AdultHuman (używając niebezpiecznego), to oczywiście kompilator użyje tych informacji, aby wybrać bardziej szczegółowe przeciążenie.

Powiązane problemy