2015-02-28 13 views
5

Mam bardzo prosty Playground:błąd Swift z rodzajowego tablicy

protocol MyProtocol {} 

struct MyType: MyProtocol {} 

class MyClass <T: MyProtocol> { 
    func myFunction(array: [T]) { 
     if let myArray = array as? [MyType] { 
      println("Double!") 
     } 
    } 
} 

let instance = MyClass<MyType>() 

let array = [MyType(), MyType()] 

instance.myFunction(array) 

Potem mówi "MyType is not a subtype of 'T'" na linii if let. Cóż, myślę jednak, że MyType i są kompatybilne.

Kiedy zmodyfikowana oświadczenie if let, to faktycznie działa:

if let first = array.first as? MyType 

Ale teraz nie mogę rzucić array do [MyType]

I (wiem, wiem, że jest statyczny specyfikacja wpisując Swift). zastanawiam się, jaki jest problem. Moje rozumienie o generycznych? Czy jest to ograniczenie języka Swift? Jeśli tak, czy jest jakiś sposób, aby to zrobić?

Z góry dziękuję.

Odpowiedz

4

Swift nie ma wbudowanego zachowania, aby spekulacyjnie konwertować zawartość tablicy z jednego dowolnego typu na inny. Będzie to zrobić tylko dwa rodzaje że wie mieć związek podtyp/supertypem:

class A { } 
class B: A { } 
let a: [A] = [B(),B()] 
// this is allowed - B is a subtype of A 
let b = a as? [B] 

let a: [AnyObject] = [1,2,3] 
// this is allowed - NSNumber is a subtype of AnyObject 
let b = a as? [NSNumber] 

struct S1 { } 
struct S2 { } 

let a = [S1(),S1()] 
// no dice - S2 is not a subtype of S1 
let b = a as? [S2] 

Protokół nie pomaga:

protocol P { } 
struct S1: P { } 
struct S2: P { } 

let a = [S1(),S1()] 
// still no good – just because S1 and S2 both conform to P 
// doesn’t mean S2 is a subtype of S1 
let b = a as? [S2] 

Twój przykład jest w zasadzie odmianą tego ostatniego . Masz tablicę typu [T] i chcesz ją przesłać do [MyType]. Ważne jest, aby zrozumieć, że użytkownik nie ma ma tablicę typu [MyProtocol]. Twój typ ogólny T jest specyficznym typem, który musi implementować MyProtocol, ale to nie to samo.

Aby zrozumieć, dlaczego nie można po prostu rzucać z dowolnego typu do innego typu, spróbuj tego kodu:

protocol P { } 
struct S: P { } 

let a: [P] = [S(),S()] 
let b = a as? [S] 

To wygeneruje błąd wykonania: „błąd krytyczny: nie można unsafeBitCast pomiędzy rodzajami różne rozmiary". Daje to wskazówkę, dlaczego można rzucić tylko tablicę zawierającą jeden typ odniesienia do podtypu - dzieje się tak dlatego, że to, co się dzieje, jest po prostu nieco rzutowane z jednego typu wskaźnika na inny. Będzie to działać dla klas super/podtypów, ale nie dla dowolnych klas, struktur lub protokołów, ponieważ mają one różne reprezentacje binarne.

+1

Jeszcze raz doskonałe wyjaśnienie. Chociaż wydaje mi się, że typy ogólne nie wydają się być dobrą koncepcją językową, ponieważ ich użycie wymaga po prostu głowy jajka. –

+0

Bardzo dziękuję za szybką i szczegółową odpowiedź. Wystarczy mi przekonać, że nie mogę tego zrobić w ten sposób, ale teraz ... jak mogę to zrobić? Dokładnie to, czego chcę (a właściwie "Double"! 'w pytaniu oryginalnym jest jego śladem,) jest klasą z rodzajami, a jej ogólny typ jest albo podwójny, albo int.Chcę wiedzieć, jaki typ minął i wrzucić go do odpowiedniego typu tablicy. Próbowałem to zrobić tak jak wyżej (i okazało się to niemożliwe, kiedy mnie uczysz). Czy masz jakieś pomysły? Wybacz mi moją chciwość. – Akkyie

+0

Być może sam to rozwiązałem, implementując niektóre funkcje protokołu. Dzięki. – Akkyie

3

Ogólne na typie podrzędnym nie jest podtypem tego samego generycznego typu nadrzędnego.

[MyProtocol] w języku Swift w rzeczywistości oznacza Array<MyProtocol> (tj. Rodzajowy). To samo dotyczy [MyType] jako skrótu do Array<MyType>. Właśnie dlatego nie oddaje się bezpośrednio drugiej osobie.

+0

Dziękuję również za szybką odpowiedź. – Akkyie

Powiązane problemy