Sealed classes in Kotlin to kolejna nowa koncepcja, której nie mieliśmy w Javie, i otwiera nowy świat możliwości.
zamknięta Klasa pozwala reprezentować ograniczone hierarchie, w których obiekt może być tylko jednego z podanych typów.
czyli mamy klasę z określoną liczbą podklas. To, co otrzymujemy w końcu, jest pojęciem bardzo podobnym do wyliczenia. Różnica polega na tym, że w enum mamy tylko jeden obiekt na typ, podczas gdy w klasach zamkniętych możemy mieć kilka obiektów tej samej klasy.
ta różnica pozwoli obiektom z zamkniętej klasy zachować stan. Przyniesie nam to pewne korzyści, które zobaczymy za chwilę, a także otworzy drzwi do niektórych funkcjonalnych pomysłów.
jak używać zamkniętych klas
implementacja zamkniętej klasy jest w rzeczywistości bardzo prosta. Jako przykład użyjmy zestawu operacji, które można zastosować do liczb całkowitych.
realizacja byłaby następująca:
tworzymy zamkniętą klasę o nazwie Operation
, która zawiera cztery rodzaje operacji: dodawanie, odejmowanie, mnożenie i dzielenie.
dobrą rzeczą jest to, że teraz when
wyrażenia będą wymagać od nas dostarczenia gałęzi dla wszystkich możliwych typów:

chcesz nauczyć się języka Kotlin?
Sprawdź mój darmowy przewodnik, aby stworzyć swój pierwszy projekt w 15 minut!
jeśli opuścisz którąś z podklas, when
będzie narzekać i nie będzie się kompilować. Jeśli zaimplementujesz je wszystkie, nie potrzebujesz instrukcji else
. I ogólnie nie będzie to zalecane, ponieważ w ten sposób jesteśmy pewni, że robimy dobrze dla nich wszystkich.
jest to również świetne w przypadku, gdy zdecydujesz się dodać nową operację, ponieważ nie powiedzie się w czasie kompilacji i nie będzie działać. Dodaj kilka dodatkowych operacji: inkrementacja i dekrementacja:
sealed class Operation { ... object Increment : Operation() object Decrement : Operation()}
zobaczysz, że kompilator ostrzega cię teraz, że istnieje problem. Wystarczy dodać gałęzie dla tych nowych operacji:
fun execute(x: Int, op: Operation) = when (op) { ... Operation.Increment -> x + 1 Operation.Decrement -> x - 1}
może zauważyłeś, że zrobiłem coś innego. Użyłem obiektów zamiast klas. Dzieje się tak dlatego, że jeśli podklasa nie zachowuje stanu, może to być po prostu obiekt. Wszystkie instancje tworzone dla tej klasy będą dokładnie takie same, ponieważ nie mogą mieć innego stanu.
następnie w wyrażeniu when
możesz pozbyć się is
w tych przypadkach. Tutaj możesz po prostu porównać obiekt, ponieważ jest tylko jedna instancja, nie musisz sprawdzać typu obiektu. To też by działało, gdybyś zachował is
dla tych też.
jeśli dobrze się nad tym zastanowić, zamknięta klasa, w której wszystkie podklasy są obiektami, byłaby taka sama jak enum.
przenoszenie efektów ubocznych do jednego punktu
efekty uboczne są bardzo powtarzającą się koncepcją w programowaniu funkcjonalnym. Programowanie funkcyjne opiera się głównie na założeniu, że dla danej funkcji te same parametry zwrócą ten sam wynik.
każdy zmodyfikowany stan może złamać to założenie. Ale każdy program musi modyfikować Stany, komunikować się z elementami wejścia / wyjścia, itp. Ważne jest więc, aby te operacje dostrzec bardzo konkretne miejsca w naszym kodzie, które można łatwo odizolować.
na przykład wszelkie operacje wykonywane w widoku Androida można uznać za efekt uboczny, ponieważ status widoków jest modyfikowany, a funkcje nie są tego świadome.
moglibyśmy stworzyć zamkniętą klasę, która pozwoliłaby nam wykonywać operacje na naszych widokach. Bazując na pomyśle naszego poprzedniego przykładu:
pamiętaj: operacje, które nie mają stanu, mogą być obiektami, ponieważ nie potrzebujemy różnych instancji.
teraz możesz utworzyć obiekt Ui
, który gromadzi wszystkie operacje interfejsu, które chcemy wykonać w widoku, ale nie wykona ich do momentu, w którym chcemy.
będziemy mieli opis tego, co chcemy zrobić, a następnie możemy utworzyć komponent, który je wykona:
class Ui(val uiOps: List = emptyList()) { operator fun plus(uiOp: UiOp) = Ui(uiOps + uiOp)}
Klasa Ui
przechowuje listę operacji i określa operator sumy, który pomoże uczynić wszystko nieco czystszym i łatwiejszym do odczytania. Teraz możemy określić listę operacji, które chcemy wykonać:
val ui = Ui() + UiOp.Show + UiOp.TranslateX(20f) + UiOp.TranslateY(40f) + UiOp.Hiderun(view, ui)
i uruchom go. Tutaj po prostu używam funkcji run
, ale w razie potrzeby może to być kompletna Klasa.
fun run(view: View, ui: Ui) { ui.uiOps.forEach { execute(view, it) }}
wyobraź sobie moc, która Ci to daje . Teraz wszystko, co robisz, to uruchamiasz operacje sekwencyjnie, ale to może być tak skomplikowane, jak jest to wymagane.
ta run
funkcja może być przekazana do innej funkcji lub klasy, a sposób wykonywania tych operacji byłby całkowicie wymienny. Pamiętaj, że możesz przekazywać funkcje jako argumenty.
podsumowanie
koncepcja zamkniętych klas jest bardzo prosta, ale to podstawa wielu nowych pomysłów, które musisz wykorzystać, jeśli nie bawiłeś się wcześniej programowaniem funkcyjnym.
muszę powiedzieć, że nie jestem jeszcze w stanie wyciągnąć jak najwięcej z zamkniętej klasy ze względu na ograniczenia mojej wiedzy w programowaniu funkcyjnym.
jeśli to wszystko pasjonuje Cię tak jak mnie, zachęcam do zapisania się na moje bezpłatne szkolenie, na którym opowiem Ci wszystko, czego potrzebujesz, aby dowiedzieć się, jak tworzyć aplikacje na Androida w Kotlinie od podstaw.