Το Throw και το Throws είναι μηχανισμοί εκπομπής και δήλωσης εξαιρέσεων στις γλώσσες προγραμματισμού. Ο τελεστής throw διακόπτει την κανονική εκτέλεση μιας συνάρτησης και μεταβιβάζει το αντικείμενο σφάλματος προς τα πάνω στη στοίβα κλήσεων. Η λέξη-κλειδί throws στην υπογραφή της συνάρτησης προειδοποιεί την καλούσα πλευρά για την πιθανότητα σφάλματος, κάνοντας τον κώδικα προβλέψιμο. Σύμφωνα με την Kotlin Documentation (2026), το throw στην Kotlin είναι έκφραση (expression) και όχι εντολή, επιτρέποντας τη χρήση του σε when-μπλοκ και στον τελεστή elvis.
Βασικά σημεία
Throw είναι ένας τελεστής που δημιουργεί μια εξαίρεση στο σημείο εκτέλεσης του προγράμματος. Όταν συναντάται το throw, η τρέχουσα ροή εκτέλεσης διακόπτεται αμέσως και ο έλεγχος μεταφέρεται στον πλησιέστερο χειριστή catch στη στοίβα κλήσεων. Εάν δεν βρεθεί χειριστής, η εφαρμογή τερματίζεται ανώμαλα. Το throw στην ανάπτυξη εφαρμογών για κινητά χρησιμοποιείται για σηματοδότηση σφαλμάτων που δεν μπορούν να αντιμετωπιστούν στο τρέχον επίπεδο αφαίρεσης — για παράδειγμα, λανθασμένη απάντηση διακομιστή, απουσία δικτύου ή μη έγκυρα ορίσματα.
Throws είναι ένας τροποποιητής στην υπογραφή της συνάρτησης (κυρίως στη Swift) που δηλώνει ότι η συνάρτηση μπορεί να εκτοξεύσει σφάλμα. Αποτελεί μέρος του μηχανισμού checked errors στη Swift: η καλούσα πλευρά υποχρεούται να διαχειριστεί το σφάλμα μέσω do-catch, try?, try! ή να σημειώσει τη δική της συνάρτηση με throws για περαιτέρω διάδοση (propagation). Στην Kotlin και τη Java το throws υπάρχει επίσης, αλλά η Kotlin το θεωρεί περιττό — όλες οι εξαιρέσεις στην Kotlin είναι unchecked, δηλαδή μπορούν να μην αντιμετωπιστούν χωρίς συντακτικό εξαναγκασμό. Σύμφωνα με την Apple Swift Documentation (2026), το throws στη Swift είναι ο μόνος τρόπος να δηλωθεί ρητά η πιθανότητα σφάλματος στον τύπο της συνάρτησης, καθιστώντας τα συμβόλαια API διαφανή για τον προγραμματιστή.
Η διαφορά μεταξύ throw και throws είναι θεμελιώδης: το throw είναι μια ενέργεια (εκπομπή εξαίρεσης κατά τον χρόνο εκτέλεσης), ενώ το throws είναι μια δήλωση (συμβόλαιο κατά τον χρόνο μεταγλώττισης). Μια συνάρτηση χωρίς throws δεν μπορεί να χρησιμοποιήσει throw — ο μεταγλωττιστής Swift θα δώσει σφάλμα. Μια συνάρτηση με throws μπορεί να μην χρησιμοποιεί throw — αυτό επιτρέπεται, αλλά είναι άσκοπο. Αυτός ο διαχωρισμός καθιστά τα throw/throws ένα ισχυρό εργαλείο σχεδιασμού API, όπου το συμβόλαιο σφάλματος είναι ορατό στην υπογραφή της συνάρτησης πριν από την κλήση της.
Στη Swift, ο τελεστής throw δέχεται οποιονδήποτε τύπο υλοποιεί το πρωτόκολλο Error. Συχνότερα πρόκειται για enum με περιπτώσεις για διαφορετικά είδη σφαλμάτων. Η Swift δεν υποστηρίζει checked exceptions σε στυλ Java — αντίθετα, ο τύπος της συνάρτησης σημειώνεται με throws και η διαχείριση ανατίθεται στην καλούσα πλευρά. Αυτό καθιστά το throw στη Swift πιο ευέλικτο, αλλά και πιο απαιτητικό για τον προγραμματιστή.
Οποιοσδήποτε τύπος που συμμορφώνεται με το πρωτόκολλο Error μπορεί να εκτοξευθεί μέσω throw. Συχνότερα οι προγραμματιστές χρησιμοποιούν enum με περιπτώσεις χωρίς συσχετισμένες τιμές (για απλά σφάλματα) ή με associated values (για μεταφορά περιεχομένου). Η Swift δεν απαιτεί το σφάλμα να είναι enum — μπορούν να χρησιμοποιηθούν struct ή class που υλοποιούν το Error, αλλά το enum είναι προτιμότερο χάρη στο exhaustive switch από την πλευρά του χειριστή. Ο μεταγλωττιστής ελέγχει ότι όλες οι περιπτώσεις έχουν αντιμετωπιστεί στο do-catch.
enum AuthError: Error {
case invalidCredentials
case tokenExpired
case accountLocked(remainingMinutes: Int)
}
func login(username: String, password: String) throws -> Session {
guard isValid(username) else {
throw AuthError.invalidCredentials
}
let response = try api.authenticate(username, password)
if response.isLocked {
throw AuthError.accountLocked(
remainingMinutes: response.lockDuration
)
}
return Session(token: response.token)
}
AuthError ορίζει τρία σενάρια: μη έγκυρα διαπιστευτήρια, ληγμένο διακριτικό και κλειδωμένο λογαριασμό με συσχετισμένη τιμή remainingMinutes. Η συνάρτηση login δηλώνεται ως throws — ο μεταγλωττιστής απαιτεί κλήση μέσω try. Μέσα στη συνάρτηση, το throw χρησιμοποιείται σε δύο σημεία: σε μη έγκυρο username και σε κλειδωμένο λογαριασμό. Η συσχετισμένη τιμή accountLocked επιτρέπει τη μεταβίβαση συγκεκριμένων δεδομένων στον χρήστη — πόσα λεπτά αναμονής μέχρι το ξεκλείδωμα. Αυτή η προσέγγιση εξαλείφει την ανάγκη για ξεχωριστά API endpoints για έλεγχο της κατάστασης κλειδώματος.
Στην Kotlin, το throw είναι μια έκφραση (expression) τύπου Nothing, όχι μια εντολή. Αυτό σημαίνει ότι το throw μπορεί να χρησιμοποιηθεί στη δεξιά πλευρά μιας ανάθεσης, μέσα σε when-εκφράσεις και στον τελεστή elvis ?:. Ο τύπος Nothing είναι ένας ειδικός υποτύπος όλων των τύπων στην Kotlin, επιτρέποντας τη χρήση του throw σε σημεία όπου απαιτείται τιμή οποιουδήποτε τύπου. Ο μεταγλωττιστής κατανοεί ότι μετά το throw η εκτέλεση δεν συνεχίζεται και δεν απαιτεί κλάδο για αυτήν την περίπτωση.
Nothing είναι ένας μοναδικός τύπος στην Kotlin που είναι υποτύπος όλων των πιθανών τύπων. Μια συνάρτηση που επιστρέφει Nothing (π.χ. TODO()) ποτέ δεν ολοκληρώνεται κανονικά — είτε εκτοξεύει πάντα εξαίρεση είτε μπαίνει σε ατέρμονο βρόχο. Αυτό καθιστά το throw φυσικό υποψήφιο για χρήση σε σημεία όπου απαιτείται τιμή: τελεστής elvis, when χωρίς else, αρχικοποίηση μεταβλητής. Αν το throw βρίσκεται σε κλάδο when, ο μεταγλωττιστής κατανοεί ότι ο κλάδος οδηγεί σε Nothing και δεν απαιτεί return ή else για αυτόν τον κλάδο.
data class Config(val apiUrl: String, val timeoutSec: Int)
class ConfigParser {
fun parse(json: String): Config {
val obj = JSONObject(json)
val url = obj.optString("apiUrl")
?: throw IllegalArgumentException("apiUrl is required")
val timeout = obj.optInt("timeoutSec", 30)
return Config(url, timeout)
}
fun getErrorMessage(code: Int): String {
return when (code) {
404 -> "Not found"
500 -> "Server error"
else -> throw IllegalArgumentException("Unknown code: $code")
}
}
}
Στο πρώτο παράδειγμα, το throw χρησιμοποιείται στον τελεστή elvis ?:: αν το πεδίο apiUrl λείπει από το JSON, η έκφραση throw διακόπτει αμέσως την εκτέλεση και εκτοξεύει IllegalArgumentException. Ο τύπος Nothing επιτρέπει στον μεταγλωττιστή να συμπεράνει τον τύπο της δεξιάς πλευράς ως String (ο elvis αναμένει String, το throw έχει τύπο Nothing, το Nothing είναι υποτύπος του String). Στο δεύτερο παράδειγμα, το throw μέσα σε when-έκφραση: αν ο κωδικός δεν αντιστοιχεί σε κανέναν από τους γνωστούς, εκτοξεύεται εξαίρεση. Ο μεταγλωττιστής κατανοεί ότι μετά το throw ο κώδικας είναι μη προσβάσιμος, επομένως ο τύπος επιστροφής της συνάρτησης String δεν παραβιάζεται.
Στη Swift, το throws αναγράφεται μετά τη λίστα παραμέτρων και πριν από το βέλος του τύπου επιστροφής. Μια συνάρτηση με throws μπορεί να καλεί μόνο άλλες throws-συναρτήσεις μέσα σε do-catch ή με try?. Αν μια throws-συνάρτηση δεν διαχειρίζεται το σφάλμα, το μεταβιβάζει στην καλούσα πλευρά. Η Swift υποστηρίζει επίσης το rethrows — έναν τροποποιητή για συναρτήσεις ανώτερης τάξης που δέχονται ένα closure με throws και προωθούν το σφάλμα του. Το rethrows σημαίνει ότι η συνάρτηση εκτοξεύει σφάλμα μόνο αν το μεταβιβασμένο closure το εκτόξευσε — η ίδια η συνάρτηση δεν δημιουργεί σφάλμα.
func mapValues<T>(
_ array: [T],
transform: (T) throws -> U
) rethrows -> [U] {
var result = [U]()
for element in array {
result.append(try transform(element))
}
return result
}
// Χρήση με throws-closure
let parsed = try mapValues(jsonStrings) { str in
let data = Data(str.utf8)
return try JSONDecoder().decode(Item.self, from: data)
}
Το rethrows επιτρέπει στη συνάρτηση mapValues να είναι ευέλικτη: δέχεται τόσο throws-closures όσο και κανονικά. Αν μεταβιβαστεί throws-closure, η κλήση mapValues απαιτεί try· αν είναι κανονικό, το try δεν χρειάζεται. Αυτό καθιστά το rethrows ιδανικό για συναρτήσεις ανώτερης τάξης, όπως map, filter, reduce, στην τυπική βιβλιοθήκη της Swift. Σύσταση της Apple: χρησιμοποιήστε rethrows για API που δέχονται throws-closures και η μοναδική πηγή σφάλματος είναι αυτό το closure. Αν η συνάρτηση μπορεί να εκτοξεύσει δικό της σφάλμα, χρησιμοποιήστε throws.
Το throws της Swift είναι πιο κοντά στις checked exceptions (όπως στην Java) — τα εκτοξευόμενα σφάλματα δηλώνονται στην υπογραφή. Η Kotlin και το Dart χρησιμοποιούν unchecked εξαιρέσεις — το throws στην υπογραφή δεν απαιτείται. Η διαφορά είναι θεμελιώδης: το checked υποχρεώνει τον προγραμματιστή σε διαχείριση (ασφαλέστερο αλλά πιο φλύαρο), το unchecked δίνει ελευθερία αλλά αυξάνει τον κίνδυνο να ξεχαστεί η διαχείριση σφάλματος. Η Swift επέλεξε checked για το throws, η Kotlin — unchecked για όλες τις εξαιρέσεις. Και οι δύο προσεγγίσεις έχουν πλεονεκτήματα: η Swift είναι ασφαλέστερη σε επίπεδο γλώσσας, η Kotlin είναι πιο συμπαγής και βολική σε αλυσίδες λειτουργικών μετασχηματισμών.
Για δομημένη διαχείριση σφαλμάτων σε εφαρμογές για κινητά, συνιστάται η δημιουργία προσαρμοσμένων τύπων σφαλμάτων αντί της χρήσης βασικών Exception ή Error. Στη Swift χρησιμοποιείται enum με το πρωτόκολλο Error, στην Kotlin — sealed class με κληρονομικότητα από Throwable (ή από Exception), στο Dart — class με κληρονομικότητα από Exception. Οι προσαρμοσμένοι τύποι επιτρέπουν την ομαδοποίηση σφαλμάτων ανά κατηγορία και τη μεταβίβαση συσχετισμένων δεδομένων.
| Γλώσσα | Τύπος σφάλματος | Ιδιαιτερότητα |
|---|---|---|
| Swift | enum: Error { ... } | Συσχετισμένες τιμές, exhaustive switch στο catch |
| Kotlin | sealed class : Throwable() | Data class για σφάλματα με πεδία, when-έκφραση |
| Dart | class implements Exception | Πεδίο Message, on-ρήτρα στο catch |
| Java | class extends Exception | Checked vs unchecked, υποχρεωτικό throws στην υπογραφή |
Κατά τον σχεδιασμό προσαρμοσμένων σφαλμάτων, ακολουθήστε τον κανόνα: ένα σφάλμα — ένα σενάριο. Μην συνδυάζετε διαφορετικές αιτίες σε έναν τύπο με σημαία String message — δημιουργήστε ξεχωριστές περιπτώσεις/υποκλάσεις για κάθε σενάριο. Αυτό επιτρέπει στην καλούσα πλευρά να διαχειρίζεται κάθε περίπτωση μέσω pattern matching (when/switch) και όχι μέσω σύγκρισης συμβολοσειρών. Στη Swift, αυτό παρέχει exhaustive checking — ο μεταγλωττιστής θα προειδοποιήσει αν δεν έχει αντιμετωπιστεί κάποια περίπτωση του enum NetworkError.
sealed class NetworkError(val message: String) : Throwable(message) {
data class Timeout(val durationMs: Long) :
NetworkError("Request timed out after ${durationMs}ms")
data class HttpError(val code: Int, val body: String?) :
NetworkError("HTTP $code")
data class NoConnection(val cause: IOException) :
NetworkError("No internet connection")
}
Η sealed class NetworkError κληρονομεί από Throwable (ο τυπικός τύπος εξαιρέσεων στην Kotlin). Κάθε υποκλάση είναι data class με δικά της πεδία: το Timeout περιέχει διάρκεια χρονικού ορίου σε χιλιοστά του δευτερολέπτου, το HttpError περιέχει κωδικό και σώμα απάντησης, το NoConnection περιέχει το αρχικό IOException. Αυτός ο σχεδιασμός επιτρέπει τη διαχείριση κάθε σφάλματος μέσω when με εξαντλητική αναζήτηση (όταν προστίθεται νέα υποκλάση, ο μεταγλωττιστής θα απαιτήσει ενημέρωση όλων των when-εκφράσεων).
Η Swift προσφέρει τρεις επιλογές κλήσης throws-συναρτήσεων, η καθεμία με το δικό της συμβόλαιο ασφαλείας. try είναι ο τυπικός τρόπος: απαιτεί do-catch ή ύπαρξη εντός throws-συνάρτησης. try? μετατρέπει το σφάλμα σε nil — το αποτέλεσμα γίνεται προαιρετικό, σε περίπτωση σφάλματος επιστρέφεται nil και ο τύπος αλλάζει από T σε T?. try! είναι υποχρεωτική εκτέλεση χωρίς διαχείριση: αν εκτοξευθεί σφάλμα, η εφαρμογή καταρρέει. Χρησιμοποιήστε try! μόνο όταν είστε απολύτως βέβαιοι ότι το σφάλμα είναι αδύνατο (π.χ. εκ των προτέρων έγκυρα δεδομένα).
let configPath = Bundle.main.path(forResource: "config", ofType: "json")!
// try? — προαιρετικό αποτέλεσμα
let data = try? Data(contentsOf: URL(fileURLWithPath: configPath))
let json = try? JSONSerialization.jsonObject(with: data ?? Data())
// try! — εγγυημένη επιτυχία (μόνο όταν είστε σίγουροι)
let decoder = JSONDecoder()
let defaultConfig = try! decoder.decode(
Config.self,
from: Config.defaultJSON
)
// try — τυπική επεξεργασία
do {
let user = try fetchUser()
showUser(user)
} catch let error as NetworkError {
showRetryAlert(error.message)
}
Στο παράδειγμα, το try! χρησιμοποιείται για εκ των προτέρων έγκυρο JSON ενσωματωμένο στο bundle της εφαρμογής — το σφάλμα αποκωδικοποίησης είναι αδύνατο με σωστή έκδοση. Το try? εφαρμόζεται για ανάγνωση αρχείου διαμόρφωσης — αν το αρχείο λείπει ή είναι κατεστραμμένο, η εφαρμογή χρησιμοποιεί προεπιλεγμένες τιμές αντί να καταρρεύσει. Το try σε do-catch χρησιμοποιείται για αιτήματα δικτύου, όπου το σφάλμα είναι αναμενόμενο και απαιτεί αντίδραση από τον χρήστη. Σύσταση: αποφύγετε το try! σε production κώδικα — χρησιμοποιήστε το μόνο για σταθερά δεδομένα που έχουν ελεγχθεί κατά τη μεταγλώττιση.
Συχνές ερωτήσεις
Throw είναι ο τελεστής που εκτοξεύει σφάλμα κατά την εκτέλεση του προγράμματος, διακόπτοντας τη ροή. Throws είναι ο τροποποιητής υπογραφής της συνάρτησης που δηλώνει ότι η συνάρτηση μπορεί να εκτοξεύσει σφάλμα. Μια συνάρτηση χωρίς throws δεν μπορεί να χρησιμοποιήσει throw. Το Throws είναι ένα συμβόλαιο σε χρόνο μεταγλώττισης, το throw είναι μια ενέργεια σε χρόνο εκτέλεσης.
Η Kotlin ακολουθεί τη φιλοσοφία των unchecked exceptions: όλες οι εξαιρέσεις μπορεί να μην αντιμετωπιστούν χωρίς συντακτικό εξαναγκασμό. Οι προγραμματιστές της Kotlin θεωρούν ότι το throws στην Java οδηγεί σε υπερβολικά try-catch μπλοκ και αγνόηση των checked exceptions μέσω άδειων catch. Ο Nothing-τύπος της Kotlin επιτρέπει τη χρήση του throw ως έκφραση, αντικαθιστώντας το throws με πιο ευέλικτο τρόπο.
Το try! επιτρέπεται μόνο όταν είστε απολύτως βέβαιοι ότι το σφάλμα είναι αδύνατο: εκ των προτέρων έγκυρο JSON από το bundle, σταθερά δεδομένα, έγκυρα URL σχήματα. Σε production κώδικα, το try! είναι η εξαίρεση, όχι ο κανόνας. Το try? είναι προτιμότερο για προαιρετικά σενάρια με τιμή επαναφοράς, ενώ το try με do-catch είναι για υποχρεωτική διαχείριση σφαλμάτων.
Rethrows είναι ένας τροποποιητής για συναρτήσεις που δέχονται throws-closures. Μια συνάρτηση με rethrows εκτοξεύει σφάλμα μόνο αν το μεταβιβασμένο closure το εκτόξευσε. Αυτό επιτρέπει σε συναρτήσεις ανώτερης τάξης (map, filter) να λειτουργούν τόσο με throws όσο και με μη-throws closures χωρίς υποχρεωτικό try στην καλούσα πλευρά.
Ναι, μέσα στο catch μπορεί να χρησιμοποιηθεί throw για προώθηση του σφάλματος ψηλότερα στη στοίβα, τυλίγοντάς το σε άλλο τύπο ή προσθέτοντας περιεχόμενο. Αυτό ονομάζεται error chaining ή rethrow. Στη Swift αρκεί ένα ακόμη throw μέσα στο catch, στην Kotlin — throw μέσα στο catch-μπλοκ. Το μπλοκ finally εκτελείται πριν από τη μεταφορά του ελέγχου παρακάτω.
Σύνοψη
Θα αναπτύξουμε μια εφαρμογή για κινητά έτοιμη για χρήση
Η IT Sectr δημιουργεί εφαρμογές iOS και Android για νεοφυείς επιχειρήσεις και επιχειρήσεις από το 2017. Θα σας συμβουλεύσουμε και θα προτείνουμε την καλύτερη λύση.
Διαβάστε επίσης