Chciałbym stworzyć abstrakcyjną funkcję w szybkim języku. Czy to możliwe?
class BaseClass {
func abstractFunction() {
// How do I force this function to be overridden?
}
}
class SubClass : BaseClass {
override func abstractFunction() {
// Override
}
}
Odpowiedzi:
W Swift nie ma koncepcji abstrakcji (jak Objective-C), ale możesz to zrobić:
źródło
assert(false, "This method must be overriden by the subclass")
.fatalError("This method must be overridden")
preconditionFailure("Bla bla bla")
w Swift, który będzie wykonywany w kompilacjach wydania, a także eliminuje potrzebę instrukcji return. EDIT: Właśnie dowiedziałem się, że ta metoda jest w zasadzie równafatalError()
, ale to jest bardziej właściwy sposób (Lepsza dokumentacja, wprowadzony w Swift wraz zprecondition()
,assert()
iassertionFailure()
, przeczytaj tutaj )To, czego potrzebujesz, to nie klasa bazowa, ale protokół.
Jeśli nie podasz abstractFunction w swojej klasie, jest to błąd.
Jeśli nadal potrzebujesz klasy bazowej do innego zachowania, możesz to zrobić:
źródło
Przenoszę sporą ilość kodu z platformy obsługującej abstrakcyjne klasy bazowe do Swift i często z tym korzystam. Jeśli to, czego naprawdę chcesz, to funkcjonalność abstrakcyjnej klasy bazowej, oznacza to, że ta klasa służy zarówno jako implementacja funkcjonalności współdzielonej klasy bazowej (w przeciwnym razie byłby to tylko interfejs / protokół) ORAZ definiuje metody, które muszą być implementowane przez klasy pochodne.
Aby to zrobić w Swift, będziesz potrzebować protokołu i klasy bazowej.
Należy zauważyć, że klasa bazowa implementuje metody współdzielone, ale nie implementuje protokołu (ponieważ nie implementuje wszystkich metod).
Następnie w klasie pochodnej:
Klasa pochodna dziedziczy sharedFunction z klasy bazowej, pomagając spełnić tę część protokołu, a protokół nadal wymaga klasy pochodnej do zaimplementowania abstractFunction.
Jedynym prawdziwym minusem tej metody jest to, że ponieważ klasa bazowa nie implementuje protokołu, jeśli masz metodę klasy bazowej, która wymaga dostępu do właściwości / metody protokołu, będziesz musiał przesłonić ją w klasie pochodnej, a stamtąd wywołać przekazywanie klasy bazowej (przez super),
self
tak aby klasa bazowa miała instancję protokołu, z którym ma wykonywać swoją pracę.Na przykład powiedzmy, że sharedFunction wymagało wywołania abstractFunction. Protokół pozostałby taki sam, a klasy wyglądałyby teraz następująco:
Teraz sharedFunction z klasy pochodnej spełnia tę część protokołu, ale klasa pochodna nadal jest w stanie współużytkować logikę klasy bazowej w dość prosty sposób.
źródło
Wydaje się, że jest to „oficjalny” sposób, w jaki Apple traktuje abstrakcyjne metody w UIKit. Przyjrzyj się
1. Umieść metodę abstrakcyjną w protokole 2. Napisz swoją klasę bazową 3. Wpisz podklasy. Oto, jak z tego wszystkiego korzystaćUITableViewController
i zobacz, jak to działaUITableViewDelegate
. Jedną z pierwszych rzeczy, które robisz, jest dodanie linii:delegate = self
. Cóż, to jest dokładnie ta sztuczka.Sprawdź w Playground, aby zobaczyć wynik.
Kilka uwag
UITableViewController
implementuje,UITableViewDelegate
ale nadal musi być rejestrowany jako delegat przez jawne ustawieniedelegate
właściwości.źródło
getComments
metodę na delegacie nadrzędnym. Istnieje kilka typów komentarzy i chcę, aby były one odpowiednie dla każdego obiektu. Tak więc chcę, aby podklasy nie kompilowały się, gdy to zrobię,delegate.getComments()
jeśli nie zastąpię tej metody. VC po prostu wie, że maParentDelegate
obiekt o nazwiedelegate
lubBaseClassX
w twoim przykładzie, aleBaseClassX
nie ma metody abstrakcyjnej. Potrzebowałbym VC, aby wiedzieć, że używa rozszerzeniaSubclassedDelegate
.Jednym ze sposobów jest użycie opcjonalnego zamknięcia zdefiniowanego w klasie bazowej, a dzieci mogą zdecydować, czy je zaimplementować, czy nie.
źródło
Cóż, wiem, że spóźniłem się na mecz i mogę wykorzystać zmiany, które się wydarzyły. Przepraszam za to.
W każdym razie chciałbym udzielić odpowiedzi, ponieważ uwielbiam testować, a rozwiązania z
fatalError()
AFAIK nie są testowalne, a te z wyjątkami są znacznie trudniejsze do przetestowania.Sugerowałbym zastosowanie szybszego podejścia. Twoim celem jest zdefiniowanie abstrakcji, która ma pewne wspólne szczegóły, ale które nie są w pełni zdefiniowane, tj. Metoda (metody) abstrakcyjna. Użyj protokołu, który definiuje wszystkie oczekiwane metody w abstrakcji, zarówno te, które są zdefiniowane, jak i te, które nie są. Następnie utwórz rozszerzenie protokołu, które implementuje metody zdefiniowane w Twoim przypadku. Wreszcie każda klasa pochodna musi implementować protokół, co oznacza wszystkie metody, ale te, które są częścią rozszerzenia protokołu, mają już swoją implementację.
Rozszerzenie przykładu o jedną konkretną funkcję:
Zauważ, że robiąc to, kompilator znów jest twoim przyjacielem. Jeśli metoda nie jest „nadpisana”, w czasie kompilacji pojawi się błąd, zamiast tych, które pojawią się w przypadku
fatalError()
wyjątków, które wystąpią w czasie wykonywania.źródło
Rozumiem, co teraz robisz, myślę, że lepiej byłoby, gdybyś użył protokołu
Następnie po prostu dostosuj się do protokołu:
Jeśli klasa jest również podklasą, protokoły są zgodne z nadklasą:
źródło
Używanie
assert
słowa kluczowego do wymuszania abstrakcyjnych metod:Jednak, jak wspomniał Steve Waddicor , prawdopodobnie chcesz
protocol
zamiast tego.źródło
Rozumiem pytanie i szukałem tego samego rozwiązania. Protokoły to nie to samo, co metody abstrakcyjne.
W protokole musisz określić, że twoja klasa jest zgodna z takim protokołem, metoda abstrakcyjna oznacza, że musisz przesłonić taką metodę.
Innymi słowy, protokoły są rodzajem opcji, musisz określić klasę bazową i protokół, jeśli nie określisz protokołu, nie musisz nadpisywać takich metod.
Metoda abstrakcyjna oznacza, że potrzebujesz klasy bazowej, ale musisz zaimplementować własną lub dwie metody, które nie są takie same.
Potrzebuję tego samego zachowania, dlatego szukałem rozwiązania. Myślę, że Swiftowi brakuje takiej funkcji.
źródło
Istnieje inna alternatywa dla tego problemu, chociaż nadal ma ona wadę w porównaniu z propozycją @ jaumard; wymaga instrukcji powrotu. Chociaż nie mam sensu tego wymagać, ponieważ polega na bezpośrednim rzuceniu wyjątku:
I wtedy:
Cokolwiek nastąpi później, jest nieosiągalne, więc nie rozumiem, dlaczego wymuszać powrót.
źródło
AbstractMethodException().raise()
?Nie wiem, czy to się przyda, ale miałem podobny problem z metodą abstrakcyjną podczas próby zbudowania gry SpritKit. To, czego chciałem, to abstrakcyjna klasa Animal, która ma metody takie jak move (), run () itp., Ale nazwy duszków (i inne funkcje) powinny być dostarczane przez dzieci klasy. Skończyło się na tym, że zrobiłem coś takiego (testowane dla Swift 2):
źródło