Sealed Class — ένας ειδικός τύπος κλάσης στην Kotlin που περιορίζει την ιεραρχία κληρονομικότητας σε ένα σταθερό σύνολο υποτύπων. Όλοι οι κληρονόμοι δηλώνονται στο ίδιο αρχείο και είναι γνωστοί στον μεταγλωττιστή, επιτρέποντας τη χρήση ενός εξαντλητικού μπλοκ when χωρίς υποχρεωτικό else κλάδο. Σύμφωνα με το Kotlin Docs, 2026, οι σφραγισμένες κλάσεις αποτελούν βασικό μηχανισμό για την αναπαράσταση περιορισμένων ιεραρχιών, όπως καταστάσεις, τύποι σφαλμάτων και συμβάντα UI.
Κύρια σημεία
Sealed Class (σφραγισμένη κλάση) — είναι μια κλάση στην Kotlin σημαδεμένη με τον τροποποιητή sealed. Ορίζει μια περιορισμένη ιεραρχία τύπων: όλοι οι πιθανοί κληρονόμοι απαριθμούνται στο ίδιο αρχείο και ο μεταγλωττιστής γνωρίζει για τον καθένα. Αυτό διακρίνει το sealed class από μια συνηθισμένη ανοικτή κλάση, της οποίας οι κληρονόμοι μπορούν να δηλωθούν οπουδήποτε.
Ο κύριος σκοπός του sealed class είναι η ασφαλής τύπου αναπαράσταση ενός πεπερασμένου συνόλου παραλλαγών. Κάθε κληρονόμος μπορεί να έχει τη δική του δομή δεδομένων, καθιστώντας το sealed class πιο ευέλικτο από το enum. Κατά τον χρόνο εκτέλεσης, το sealed class είναι μια συνηθισμένη αφηρημένη κλάση, ο μεταγλωττιστής επιβάλλει περιορισμούς μόνο στο στάδιο μεταγλώττισης.
Το Sealed class είναι ιδιαίτερα χρήσιμο στην αρχιτεκτονική Android εφαρμογών: καταστάσεις UI, αποτελέσματα αιτημάτων δικτύου, συμβάντα πλοήγησης τύπου Intent και φυσικά ιεραρχίες σφαλμάτων — τυπικά σενάρια εφαρμογής.
Κατά τη μεταγλώττιση, το sealed class βελτιστοποιείται σε πίνακα μεταβάσεων για εκφράσεις when, καθιστώντας το πιο αποδοτικό από τις αλυσίδες if-else. Σε συνδυασμό με το data class, κάθε κληρονόμος μπορεί να περιέχει όχι μόνο κατάσταση, αλλά και μεθόδους, επιτρέποντας τη δημιουργία αυτοτεκμηριούμενων μοντέλων τομέα χωρίς boilerplate κώδικα.
Το Sealed class είναι επίσης αποτελεσματικό για την αναπαράσταση πεπερασμένων αυτομάτων (state machine) σε εφαρμογές για κινητά. Κάθε κατάσταση — ξεχωριστός κληρονόμος με μοναδικές παραμέτρους, και οι μεταβάσεις μεταξύ καταστάσεων ελέγχονται μέσω της έκφρασης when. Ο μεταγλωττιστής εγγυάται ότι όλες οι πιθανές καταστάσεις έχουν υποστεί επεξεργασία, εξαλείφοντας σφάλματα χρόνου εκτέλεσης κατά την αλλαγή κατάστασης UI ή επιχειρηματικής λογικής.
Οι αρχάριοι προγραμματιστές Kotlin συχνά μπερδεύουν το sealed class με το enum, καθώς και τα δύο περιορίζουν το σύνολο τιμών. Ωστόσο, υπάρχει θεμελιώδης διαφορά μεταξύ τους: το enum — είναι ένα σύνολο σταθερών του ίδιου τύπου, το sealed class — μια ιεραρχία διαφορετικών τύπων.
Enum είναι βέλτιστο όταν όλες οι παραλλαγές είναι σταθερές χωρίς πρόσθετη δομή. Για παράδειγμα, ημέρες της εβδομάδας, καταστάσεις παραγγελίας ή τύποι ενεργειών χωρίς παραμέτρους. Κάθε τιμή enum είναι ένα singleton με σταθερό όνομα.
Sealed class απαιτείται όταν κάθε παραλλαγή έχει τα δικά της δεδομένα. Για παράδειγμα, ένα σφάλμα δικτύου περιέχει κωδικό απόκρισης, ένα σφάλμα ανάλυσης — λεπτομέρειες, και ένα σφάλμα εξουσιοδότησης — μήνυμα. Κάθε κληρονόμος του sealed class είναι ένας ξεχωριστός τύπος με μοναδικά πεδία.
// Enum — όλες οι παραλλαγές ενός τύπου
enum class Status { LOADING, SUCCESS, ERROR }
// Sealed class — κάθε παραλλαγή με δικά της δεδομένα
sealed class UiState<out T> {
object Loading : UiState<Nothing>()
data class Success<T>(val data: T) : UiState<T>()
data class Error(val message: String) : UiState<Nothing>()
}
Από την Kotlin 1.5, υπάρχει η δυνατότητα δήλωσης sealed interface. Αυτό επεκτείνει την έννοια sealed στις διεπαφές: το sealed interface έχει επίσης σταθερό σύνολο υλοποιήσεων, αλλά υποστηρίζει πολλαπλή κληρονομικότητα.
Sealed interface είναι βολικό όταν οι κληρονόμοι πρέπει να υλοποιούν πολλαπλές συμβάσεις ταυτόχρονα. Για παράδειγμα, ένα συμβάν UI μπορεί να είναι ταυτόχρονα κλικάσιμο και ιχνηλάσιμο. Με sealed class θα έπρεπε να επιλέξετε μία βασική κλάση, με sealed interface ο κληρονόμος υλοποιεί και τα δύο.
Sealed class είναι μια κλάση, επομένως κάθε κληρονόμος μπορεί να έχει μόνο έναν γονέα. Το Sealed interface λύνει αυτό το πρόβλημα, αλλά δεν μπορεί να περιέχει κατάσταση. Η επιλογή μεταξύ τους εξαρτάται από την εργασία: χρειάζεται κοινή λογική με πεδία — sealed class, χρειάζεται ευελιξία συμβάσεων — sealed interface.
sealed interface ScreenEvent {
data class Refresh(val force: Boolean) : ScreenEvent
data class Navigate(val route: String) : ScreenEvent
data class ShowError(val toast: String) : ScreenEvent
}
sealed interface AnalyticsEvent {
val name: String
val params: Map<String, Any>
}
// Ο κληρονόμος υλοποιεί και τις δύο διεπαφές
data class LoginClicked(
override val name: String = "login_click",
override val params: Map<String, Any> = emptyMap()
) : ScreenEvent, AnalyticsEvent
Μία από τις κύριες εφαρμογές του sealed class στην ανάπτυξη εφαρμογών για κινητά — ασφαλής τύπου ιεραρχία σφαλμάτων. Αντί να ρίχνετε εξαιρέσεις διαφορετικών τύπων ή να χρησιμοποιείτε γενικό Exception, το sealed class συλλέγει όλα τα πιθανά σφάλματα τομέα σε έναν τύπο.
Δημιουργήστε ένα sealed class DomainError και απαριθμήστε όλους τους τύπους αποτυχίας ως κληρονόμους. Κάθε κληρονόμος περιέχει μόνο τα δεδομένα που έχουν νόημα για αυτόν τον τύπο σφάλματος. Ο μεταγλωττιστής εγγυάται ότι κατά την επεξεργασία του σφάλματος δεν θα ξεχάσετε καμία παραλλαγή.
Ας εξετάσουμε μια εφαρμογή με εξουσιοδότηση, όπου είναι δυνατά διαφορετικά σενάρια αποτυχίας: λάθος κωδικός, αποκλεισμός λογαριασμού, πρόβλημα διακομιστή. Το Sealed class τα ενώνει σε έναν ενιαίο τύπο με εξαντλητική διαχείριση.
sealed class AuthError {
data class InvalidCredentials(
val attempts: Int
) : AuthError()
data class AccountBlocked(
val until: Long
) : AuthError()
data class NetworkFailure(
val cause: Throwable
) : AuthError()
object ServerError : AuthError()
}
fun handleError(error: AuthError): String = when (error) {
is AuthError.InvalidCredentials ->
"Απομένουν προσπάθειες: ${3 - error.attempts}"
is AuthError.AccountBlocked ->
"Η πρόσβαση αποκλείστηκε έως ${Date(error.until)}"
is AuthError.NetworkFailure ->
"Ελέγξτε τη σύνδεση: ${error.cause.localizedMessage}"
AuthError.ServerError ->
"Ο διακομιστής είναι προσωρινά μη διαθέσιμος"
}
Sealed class έχει γίνει standard εργαλείο στην αρχιτεκτονική εφαρμογών Android. Ας εξετάσουμε τρία βασικά μοτίβα όπου το sealed class είναι απαραίτητο στην ανάπτυξη εφαρμογών για κινητά.
Ξεχωριστά, αξίζει να αναφερθεί η εφαρμογή του sealed class στην Clean Architecture. Κάθε επίπεδο (data, domain, presentation) χρησιμοποιεί sealed class για τους δικούς του τύπους σφαλμάτων, και οι mapper μετατρέπουν ένα sealed class σε άλλο. Για παράδειγμα, το DataError από το επίπεδο δεδομένων αντιστοιχίζεται σε DomainError για την επιχειρηματική λογική και στη συνέχεια σε UiState για το επίπεδο παρουσίασης. Αυτό διατηρεί την ασφάλεια τύπου σε όλα τα επίπεδα της εφαρμογής και εγγυάται ότι κανένα σφάλμα δεν θα παραμείνει ανεπεξέργαστο.
Δοκιμή του sealed class απαιτεί ειδική προσέγγιση, καθώς κάθε κληρονόμος είναι ξεχωριστός τύπος με δική του κατάσταση. Συνιστάται η σύνταξη παραμετροποιημένων δοκιμών που διέρχονται από όλους τους κληρονόμους του sealed class. Αυτό εγγυάται ότι οι εκφράσεις when καλύπτουν όλες τις παραλλαγές, συμπεριλαμβανομένων των νέων που προστίθενται κατά την επέκταση της ιεραρχίας.
Για δοκιμές UI, το sealed class ως UiState επιτρέπει τον έλεγχο εμφάνισης κάθε κατάστασης: Loading δείχνει spinner, Content — δεδομένα, Error — μήνυμα σφάλματος. Δεδομένου ότι το sealed class είναι πεπερασμένο, η κάλυψη δοκιμών όλων των καταστάσεων παρέχει πλήρη βεβαιότητα για την ορθότητα της λογικής UI.
Παρά την απλότητα της έννοιας, οι προγραμματιστές κάνουν τακτικά λάθη κατά τον σχεδιασμό ιεραρχιών sealed class. Ας εξετάσουμε τα κύρια προβλήματα και τρόπους αποφυγής τους.
Συχνές Ερωτήσεις
Ναι, το sealed class μπορεί να περιέχει αφηρημένες μεθόδους, και κάθε κληρονόμος υποχρεούται να τις υλοποιήσει. Είναι βολικό όταν όλες οι παραλλαγές πρέπει να παρέχουν μια κοινή διεπαφή, αλλά με διαφορετική λογική εκτέλεσης.
Στην Java 17+ εμφανίστηκαν σφραγισμένες κλάσεις και διεπαφές με τον τροποποιητή sealed. Το Android υποστηρίζει προς το παρόν μερικώς την Java 17, αλλά σε έργα Kotlin, το sealed class είναι διαθέσιμο από την Kotlin 1.0 χωρίς περιορισμούς.
Ναι, ένα sealed class μπορεί να είναι κληρονόμος άλλου. Η ιεραρχία sealed class παραμένει πεπερασμένη: ο μεταγλωττιστής γνωρίζει όλους τους κληρονόμους σε κάθε επίπεδο. Αυτό επιτρέπει τη δημιουργία λεπτομερών ταξινομήσεων σφαλμάτων.
Το Sealed class δεν δημιουργεί πρόσθετο κόστος κατά τον χρόνο εκτέλεσης. Ο μεταγλωττιστής βελτιστοποιεί τις εκφράσεις when με sealed class σε πίνακες μεταβάσεων (tableswitch), που είναι ταχύτεροι από αλυσίδες if-else. Η απόδοση είναι ταυτόσημη με το enum.
Κάθε κληρονόμος του sealed class δοκιμάζεται ξεχωριστά. Δεδομένου ότι το sealed class είναι πεπερασμένο, μπορεί να γραφτεί μια παραμετροποιημένη δοκιμή που διέρχεται από όλες τις παραλλαγές. Αυτό παρέχει πλήρη κάλυψη των κλάδων των μπλοκ when.
Σύνοψη
Θα αναπτύξουμε μια εφαρμογή για κινητά έτοιμη για χρήση
Η IT Sectr δημιουργεί εφαρμογές iOS και Android για νεοφυείς επιχειρήσεις και επιχειρήσεις από το 2017. Θα σας συμβουλεύσουμε και θα προτείνουμε την καλύτερη λύση.
Διαβάστε επίσης