Na początek wyjaśnijmy błąd:
Dane:
class Foo<T:Hashable> { }
class SubFoo<String> : Foo<String> { }
Mylące częścią jest to, że możemy spodziewać się „string” oznacza Swift określoną strukturę, która przechowuje zbiór znaków. Ale nie jest.
W tym miejscu "String" jest nazwą ogólnego typu, który nadaliśmy naszej nowej podklasie, SubFoo
. Staje się to oczywiste, gdy wykonujemy pewne zmiany:
class SubFoo<String> : Foo<T> { }
Linia ta generuje błąd dla T
jak stosowanie typu nierejestrowanej.
Następnie gdybyśmy zmienić linię do tego:
class SubFoo<T> : Foo<T> { }
Wracamy do tego samego błędu pierwotnie miał, „T” nie odpowiada „Hashable”. Jest to oczywiste, ponieważ T nie jest myląco nazwą istniejącego typu Swift, który jest zgodny z "Hashable". To oczywiste, że "T" to rodzajowy.
Kiedy piszemy "String", jest to po prostu nazwa zastępcza dla typu ogólnego, a nie faktycznie typu String
, który istnieje w Swift.
Jeśli chcemy inną nazwę dla określonego typu rodzajowego klasy, odpowiednie podejście jest prawie na pewno typealias
:
class Foo<T:Hashable> {
}
typealias StringFoo = Foo<String>
To jest całkowicie poprawny Swift i kompiluje dobrze.
Jeżeli zamiast tego, co chcemy jest rzeczywiście podklasy i dodać metody lub właściwości do klasy ogólnej, to co potrzebujemy to klasa lub protokół, który sprawi, że nasz generic dokładniej do czego potrzebujemy.
Wracając do pierwotnego problemu, niech najpierw pozbyć się błędu:
class Foo<T: Hashable>
class SubFoo<T: Hashable> : Foo<T> { }
To jest całkowicie poprawny Swift. Ale może nie być szczególnie przydatne dla tego, co robimy.
Jedynym powodem, nie możemy wykonać następujące czynności:
class SubFoo<T: String> : Foo<T> { }
jest po prostu dlatego, że nie jest String
Swift klasa - to struktura. A to nie jest dozwolone dla żadnej struktury.
Jeśli piszemy nowy protokół, który dziedziczy Hashable
, możemy użyć, że:
protocol MyProtocol : Hashable { }
class Foo<T: Hashable> { }
class SubFoo<T: MyProtocol> : Foo<T> { }
To jest całkowicie poprawny.
Należy również pamiętać, że w rzeczywistości nie muszą dziedziczyć Hashable
:
protocol MyProtocol { }
class Foo<T: Hashable> { }
class SubFoo<T: Hashable, MyProtocol> { }
Jest również całkowicie poprawny.
Należy jednak pamiętać, że z dowolnego powodu Swift nie zezwoli na korzystanie z zajęć tutaj. Na przykład:
class MyClass : Hashable { }
class Foo<T: Hashable> { }
class SubFoo<T: MyClass> : Foo<T> { }
Swift tajemniczo skarży się, że „T” nie odpowiada „Hashable” (nawet gdy dodamy kod niezbędny, aby tak było
w końcu prawo. a podejście najbardziej odpowiednie dla Swift ma polegać na pisaniu nowego protokołu, który dziedziczy po "Hashable" i dodaje do niego dowolną funkcjonalność.
Nie powinno być absolutnie ważne, aby nasza podklasa akceptowała numer String
. Powinno być ważne, że wha Podejmij naszą podklasę, ma niezbędne metody i właściwości, których potrzebujemy do tego, co robimy.
To nie to. Istnieje kwestia/ograniczenie dotyczące Swift, zobacz http://stackoverflow.com/questions/24138359/limitation-with-classes-derived-from-generic-classes-in-swift – Ixx
@lxx: Ick. Podejrzewam, że to nadal jest * przyczyna *, ale chcesz innego rozwiązania. Edytowałem swoją odpowiedź z możliwym rozwiązaniem, aby spróbować. –
Cóż, tak, rozwiązanie z parametrem throw away działa, ale nie próbowałem go, ponieważ jest brzydki. Z drugiej strony, próbowałem ponownie rozwiązania typalias i działa teraz. Wystąpił problem, ponieważ (wybrana odpowiedź) używa Int jako symbolu zastępczego, dlatego próbowałem używać parametru String, który powodował błędy. Zamieniłem to na T i zadeklarowałem typy poniżej klasy, a nie kompilacje. Wciąż jest to obejście problemu. I tak dziękuję za skierowanie mnie z powrotem do właściwego rozwiązania :) – Ixx