Try-Catch — μια δομή σύλληψης εξαιρέσεων που επιτρέπει την εκτέλεση δυνητικά επικίνδυνου κώδικα σε προστατευμένο μπλοκ και τη σωστή διαχείριση σφαλμάτων χωρίς απότομο τερματισμό του προγράμματος. Το μπλοκ try περιέχει κώδικα που μπορεί να ρίξει εξαίρεση, το catch την συλλαμβάνει και εκτελεί λογική ανάκτησης. Σύμφωνα με το Apple Swift Documentation (2026), το μπλοκ finally εκτελείται ανεξάρτητα από το αν ρίχθηκε εξαίρεση ή όχι, εγγυώμενο την απελευθέρωση πόρων.
Κύρια Σημεία
Try-Catch — μια θεμελιώδης δομή δομημένης διαχείρισης εξαιρέσεων, παρούσα στις περισσότερες σύγχρονες γλώσσες προγραμματισμού. Αποτελείται από τρία μπλοκ: try (προσπάθεια εκτέλεσης επικίνδυνου κώδικα), catch (σύλληψη και διαχείριση της εξαίρεσης) και προαιρετικό finally (ολοκλήρωση). Η ιδέα της δομής είναι ο διαχωρισμός της λογικής επιχειρηματικών λειτουργιών από τη λογική διαχείρισης σφαλμάτων, κάνοντας τον κώδικα πιο ευανάγνωστο και προβλέψιμο.
Η ιδέα πρωτοεμφανίστηκε στη γλώσσα C++ ως try/catch, στη συνέχεια υιοθετήθηκε από τις Java, C#, Swift, Kotlin, Dart, Python, JavaScript και άλλες γλώσσες. Κάθε γλώσσα προσθέτει τα δικά της χαρακτηριστικά: στη Swift το μπλοκ catch πρέπει να είναι εξαντλητικό, στην Kotlin το try-catch μπορεί να είναι έκφραση (expression), στη Dart το finally είναι υποχρεωτικό για πόρους ροής. Παρά τις διαφορές, η βασική αρχή είναι ίδια: το σφάλμα διαχειρίζεται όσο το δυνατόν πιο κοντά στο σημείο εμφάνισης, όχι καθολικά.
Η χρήση του Try-Catch είναι ιδιαίτερα σημαντική στην κινητή ανάπτυξη, όπου εξωτερικοί παράγοντες — απώλεια δικτύου, λανθασμένη απόκριση διακομιστή, έλλειψη μνήμης — συμβαίνουν συνεχώς. Η σωστή διαχείριση εξαιρέσεων αποτρέπει crashes της εφαρμογής και εξασφαλίζει σωστή UX: ο χρήστης λαμβάνει μήνυμα σφάλματος αντί για ξαφνικό κλείσιμο της εφαρμογής. Σύμφωνα με το Google Android Kotlin Style Guide (2026), κάθε συνάρτηση που μπορεί να ρίξει εξαίρεση πρέπει είτε να τη διαχειρίζεται μέσω try-catch είτε να δηλώνει throws στην υπογραφή.
Ο μηχανισμός εκτέλεσης του try-catch βασίζεται στο ξετύλιγμα στοίβας (stack unwinding). Όταν μέσα στο μπλοκ try ρίχνεται μια εξαίρεση μέσω του τελεστή throw (ή ως αποτέλεσμα συστήματος σφάλματος), η κανονική ροή εκτέλεσης διακόπτεται αμέσως. Η εκτέλεση μετακινείται σε υψηλότερο επίπεδο στη στοίβα κλήσεων αναζητώντας ένα κατάλληλο μπλοκ catch. Οι σύγχρονες γλώσσες αναζητούν catch με τύπο που αντιστοιχεί στον τύπο της ριγμένης εξαίρεσης, χρησιμοποιώντας μηχανισμό αντιστοίχισης τύπων (type matching).
Εάν βρεθεί το κατάλληλο catch, εκτελείται το σώμα του, μετά το οποίο η εκτέλεση συνεχίζεται μετά από ολόκληρη τη δομή try-catch-finally. Εάν δεν βρεθεί catch, η εξαίρεση ανεβαίνει ψηλότερα στη στοίβα και μπορεί να διαχειριστεί σε υψηλότερο επίπεδο — μέχρι τον καθολικό χειριστή που στη φορητή εφαρμογή εμφανίζει ένα διάλογο σφάλματος στο χρήστη. Εάν η εξαίρεση δεν διαχειριστεί πουθενά, η εφαρμογή καταρρέει. Γι' αυτό ακριβώς η σωστή διαχείριση όλων των πιθανών τύπων εξαιρέσεων είναι κρίσιμη για τη σταθερότητα της εφαρμογής.
fun readUserData(): User {
return try {
val response = api.fetchUser()
parseUser(response)
} catch (e: IOException) {
logError("Σφάλμα δικτύου", e)
throw AppException("Αποτυχία φόρτωσης δεδομένων")
} catch (e: JsonParseException) {
logError("Σφάλμα ανάλυσης", e)
return User.default()
} finally {
closeLoadingIndicator()
}
}
Ο κώδικας πρώτα προσπαθεί να εκτελέσει ένα αίτημα API και να αναλύσει την απόκριση. Εάν προκύψει IOException (πρόβλημα δικτύου), η εξαίρεση καταγράφεται και προωθείται ως AppException. Εάν JsonParseException — επιστρέφεται ο προεπιλεγμένος χρήστης. Το μπλοκ finally εγγυημένα κρύβει τον δείκτη φόρτωσης, αποτρέποντας διαρροή στοιχείων UI στην οθόνη.
Στη Swift, η διαχείριση σφαλμάτων υλοποιείται μέσω του πρωτοκόλλου Error (πρώην ErrorType). Οποιοσδήποτε τύπος συμβατός με το Error μπορεί να ριχθεί μέσω του τελεστή throw. Μια συνάρτηση που μπορεί να ρίξει σφάλμα επισημαίνεται με τη λέξη-κλειδί throws στην υπογραφή. Η κλήση μιας τέτοιας συνάρτησης απαιτεί το πρόθεμα try (για ρητό try-catch), try? (προαιρετικό αποτέλεσμα) ή try! (εξαναγκασμένη εκτέλεση χωρίς διαχείριση σφάλματος).
enum NetworkError: Error {
case noConnection
case serverError(code: Int)
case timeout
}
func fetchUser(id: Int) throws -> User {
guard isConnected() else {
throw NetworkError.noConnection
}
let data = try performRequest(path: "/users/\(id)")
return try decodeUser(from: data)
}
do {
let user = try fetchUser(id: 42)
updateUI(user)
} catch NetworkError.noConnection {
showOfflineAlert()
} catch let error as NetworkError {
showError("Δίκτυο " + error.localizedDescription)
} catch {
showGenericError()
}
Enum NetworkError υλοποιεί το πρωτόκολλο Error, ορίζοντας τρεις περιπτώσεις: noConnection, serverError με κωδικό και timeout. Η συνάρτηση fetchUser είναι σημειωμένη με throws: πρώτα ελέγχει τη σύνδεση, στη συνέχεια εκτελεί το αίτημα και την ανάλυση. Στο μπλοκ do-catch, τρία catch διαχειρίζονται διαφορετικά σενάρια: τη συγκεκριμένη περίπτωση noConnection, τον γενικό τύπο NetworkError και όλα τα άλλα σφάλματα. Αυτό επιτρέπει την εμφάνιση διαφορετικών μηνυμάτων στο χρήστη ανάλογα με τον τύπο του προβλήματος.
Η Kotlin κληρονόμησε το try-catch-finally από τη Java, αλλά πρόσθεσε μια σημαντική διαφορά: στην Kotlin, το try-catch είναι μια έκφραση (expression), όχι μια εντολή (statement). Αυτό σημαίνει ότι το αποτέλεσμα του μπλοκ try ή του μπλοκ catch μπορεί να εκχωρηθεί σε μια μεταβλητή. Η τελευταία έκφραση στο μπλοκ try γίνεται το αποτέλεσμα σε επιτυχία, η τελευταία έκφραση στο catch — σε σφάλμα. Εάν το σφάλμα δεν διαχειριστεί από κανένα catch, η εξαίρεση προωθείται πάνω στη στοίβα.
sealed class Result<out T> {
data class Success<out T>(val data: T) : Result<T>()
data class Error(val exception: Throwable) : Result<Nothing>()
}
fun loadData(): Result<List<Item>> {
return try {
val response = api.getItems()
Result.Success(response.toList())
} catch (e: HttpException) {
Log.e("HTTP ", e)
Result.Error(e)
} catch (e: IOException) {
Log.e("Δίκτυο ", e)
Result.Error(e)
}
}
Στο παράδειγμα, η sealed class Result περιβάλλει μια επιτυχημένη απόκριση ή σφάλμα. Η συνάρτηση loadData χρησιμοποιεί το try-catch ως έκφραση: σε επιτυχία επιστρέφει Result.Success, σε εξαίρεση HttpException ή IOException — Result.Error με καταγραφή. Αυτή η προσέγγιση επιτρέπει στην καλούσα πλευρά να διαχειρίζεται σφάλματα χωρίς εξαιρέσεις — μέσω της έκφρασης when βάσει του τύπου Result. Αυτό είναι ιδιαίτερα βολικό στο Jetpack Compose για εμφάνιση διαφορετικών καταστάσεων UI (Loading, Success, Error) μέσω StateFlow και collectAsState.
Η Dart υποστηρίζει try-catch-finally με σύνταξη παρόμοια με τη Java, αλλά με την προσθήκη της ρήτρας on για φιλτράρισμα βάσει τύπου εξαίρεσης χωρίς καθορισμό μεταβλητής. Αυτό είναι βολικό όταν η ίδια η εξαίρεση δεν χρειάζεται — μόνο το γεγονός του τύπου της είναι σημαντικό. Η Dart υποστηρίζει επίσης το μπλοκ catch με δύο παραμέτρους: το αντικείμενο εξαίρεσης και StackTrace, που είναι χρήσιμο για καταγραφή ολόκληρης της αλυσίδας κλήσεων.
import 'dart:io';
import 'dart:convert';
class UserRepository {
Future<User> fetchUser(String id) async {
try {
final client = HttpClient();
final request = await client.getUrl(
Uri.parse('https://api.example.com/users/$id')
);
final response = await request.close();
final body = await response.transform(utf8.decoder).join();
return User.fromJson(json.decode(body));
} on SocketException catch (e, stackTrace) {
log("No internet", e, stackTrace);
throw AppException("Connection failed");
} on FormatException {
throw AppException("Invalid response format");
} finally {
client.close();
}
}
}
Το SocketException συλλαμβάνεται με το αντικείμενο και StackTrace για λεπτομερή καταγραφή, στη συνέχεια προωθείται ως AppException. Το FormatException συλλαμβάνεται χωρίς μεταβλητή — αρκεί να γνωρίζουμε ότι η μορφή της απόκρισης είναι λανθασμένη. Το μπλοκ finally κλείνει εγγυημένα το HttpClient, αποτρέποντας διαρροή socket. Στο Flutter, αυτή η προσέγγιση είναι ιδιαίτερα σημαντική για δοκιμές Widget, όπου οι μη διαχειριζόμενες εξαιρέσεις στο State.initState οδηγούν σε κατάρρευση ολόκληρης της συνεδρίας δοκιμής.
Ακόμα και έμπειροι προγραμματιστές κάνουν λάθη κατά την εργασία με try-catch που οδηγούν σε διαρροή μνήμης, κρυφά bugs ή ακατάλληλη συμπεριφορά της εφαρμογής. Ας εξετάσουμε πέντε συνηθισμένα προβλήματα στην κινητή ανάπτυξη.
Κενό catch — μία από τις χειρότερες πρακτικές. Η εξαίρεση καταπίνεται, η εφαρμογή συνεχίζει να λειτουργεί σε λανθασμένη κατάσταση και ο προγραμματιστής δεν μαθαίνει για το πρόβλημα. Πάντα τουλάχιστον καταγράψτε την εξαίρεση. Στην Kotlin χρησιμοποιήστε catch(e: Exception) { Log.e(...) }, στη Swift — catch { print($0) }. Στη Dart, το ελάχιστα αποδεκτό catch πρέπει να καλεί debugPrint ή να γράφει στο Crashlytics.
Η σύλληψη όλων των εξαιρέσεων μέσω catch (Exception e) χωρίς διάκριση τύπων κρύβει απροσδόκητα σφάλματα — NullPointerException, OutOfMemoryError, StackOverflowError. Πιάστε μόνο εκείνους τους τύπους που περιμένετε και μπορείτε να διαχειριστείτε. Για τα υπόλοιπα, επιτρέψτε τη διάδοση προς τα πάνω. Στην κινητή ανάπτυξη, τα συγκεκριμένα catch για IOException, TimeoutException, AuthException δίνουν πιο ουσιαστικά μηνύματα στο χρήστη.
Οι πόροι — αρχεία, sockets, δρομείς ΒΔ, κινούμενα σχέδια — πρέπει να απελευθερώνονται στο finally ή στο μπλοκ use (AutoCloseable). Οι προγραμματιστές συχνά ξεχνούν να κλείσουν πόρους σε περίπτωση εξαίρεσης, που οδηγεί σε διαρροές. Στην Kotlin χρησιμοποιήστε .use { } για πόρους Closeable, στη Swift — defer { }, στη Dart — await using από το πακέτο async. Το μπλοκ finally εγγυάται την απελευθέρωση ακόμα και κατά τη ρίψη εξαίρεσης μέσα στο catch.
Σε ασύγχρονο κώδικα, το try-catch δεν συλλαμβάνει εξαιρέσεις από άλλα νήματα. Στην Kotlin Coroutines χρησιμοποιήστε CoroutineExceptionHandler ή SupervisorJob. Στη Swift async/await — do-catch μέσα στο Task. Στο Flutter — runZonedGuarded για καθολική σύλληψη. Η αγνόηση αυτού του κανόνα είναι αιτία δύσκολα αναπαραγώγιμων crashes στην παραγωγή.
Η διαχείριση της εξαίρεσης δεν πρέπει να μπλοκάρει τη διεπαφή χρήστη για αόριστο χρονικό διάστημα. Δείξτε στο χρήστη ένα συγκεκριμένο μήνυμα και δώστε τη δυνατότητα επανάληψης της λειτουργίας. Snackbar με κουμπί Retry στην Kotlin/Compose, UIAlertController με action στη Swift, SnackBar με action στο Flutter — ελάχιστα επαρκές UX για σφάλματα δικτύου ή διακομιστή. Αποφύγετε γενικούς διαλόγους „Παρουσιάστηκε σφάλμα" χωρίς δυνατότητα ανάκτησης.
Συχνές Ερωτήσεις
Το try-catch χρησιμοποιεί εξαιρέσεις και ξετύλιγμα στοίβας για διαχείριση σφαλμάτων, που μπορεί να είναι δαπανηρό σε απόδοση με μεγάλο αριθμό σφαλμάτων. Result Type είναι ένας τύπος-δοχείο (Success ή Failure) που διαχειρίζεται μέσω pattern matching χωρίς ξετύλιγμα στοίβας, πιο αποδοτικό για αναμενόμενα σφάλματα.
Το finally είναι υποχρεωτικό εάν το μπλοκ try ανοίγει πόρους (αρχεία, sockets, δρομείς) που πρέπει να κλείσουν. Εάν δεν ανοίγονται πόροι, το finally δεν απαιτείται. Σε σύγχρονες γλώσσες χρησιμοποιήστε AutoCloseable/use/defer για αυτόματο κλείσιμο πόρων χωρίς finally. Το μπλοκ use στην Kotlin και Swift αντικαθιστά το finally για αντικείμενα Closeable.
Σε κανονική ροή (χωρίς εξαίρεση), το try-catch πρακτικά δεν επηρεάζει την απόδοση — η JVM και ο μεταγλωττιστής Swift βελτιστοποιούν αυτήν την περίπτωση. Αλλά κατά τη ρίψη εξαίρεσης, συμβαίνει ξετύλιγμα στοίβας, το οποίο μπορεί να διαρκέσει 10–100 µs ανάλογα με το βάθος της στοίβας. Μην χρησιμοποιείτε εξαιρέσεις για έλεγχο ροής εκτέλεσης — αυτό είναι αντ-πρότυπο.
Σε κορουτίνες χρησιμοποιήστε try-catch μέσα στο coroutineScope ή CoroutineExceptionHandler για καθολική σύλληψη. Το SupervisorJob αποτρέπει την ακύρωση της γονικής κορουτίνας σε σφάλμα στην παιδική κορουτίνα. Για launch χρησιμοποιήστε CoroutineExceptionHandler, για async — try-catch γύρω από το await().
Τα πολλαπλά catch είναι προτιμότερα: ο κώδικας διαβάζεται γραμμικά, κάθε μπλοκ διαχειρίζεται έναν τύπο εξαίρεσης. Ένα catch με if-else είναι δυσκολότερο στη συντήρηση, μπορεί εύκολα να παραλειφθεί ένας νέος τύπος εξαίρεσης. Στη Swift, τα πολλαπλά catch είναι υποχρεωτικά για εξαντλητική διαχείριση του enum Error, στην Kotlin δεν υπάρχουν περιορισμοί, αλλά η βέλτιστη πρακτική είναι ξεχωριστό catch ανά τύπο.
Σύνοψη
Θα αναπτύξουμε μια εφαρμογή για κινητά έτοιμη για χρήση
Η IT Sectr δημιουργεί εφαρμογές iOS και Android για νεοφυείς επιχειρήσεις και επιχειρήσεις από το 2017. Θα σας συμβουλεύσουμε και θα προτείνουμε την καλύτερη λύση.
Διαβάστε επίσης