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 је посебно важно у мобилном развоју, где спољни фактори — губитак мреже, неисправан одговор сервера, недовољно меморије — стално настају. Исправна обрада изузетака спречава краш апликације и обезбеђује коректан 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, спречавајући цурење сокета. У Flutter-у је овај приступ посебно важан за Widget тестирање, где необрађени изузеци у State.initState доводе до пада целе сесије тестирања.
Чак и искусни програмери праве грешке при раду са try-catch које доводе до цурења меморије, скривених багова или неадекватног понашања апликације. Размотримо пет најчешћих проблема у мобилном развоју.
Празан 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 дају смисленије поруке кориснику.
Ресурси — датотеке, сокети, курсори БД, анимације — треба да се ослобађају у 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 за глобално хватање. Игнорисање овог правила је узрок тешко репродуцибилних крашева у продукцији.
Обрада изузетка не сме да блокира кориснички интерфејс на неодређено време. Прикажите кориснику конкретну поруку и дајте могућност понављања операције. Snackbar са дугметом Retry у Kotlin/Compose, UIAlertController са action у Swift-у, SnackBar са action у Flutter-у — минимално довољан UX за грешке мреже или сервера. Избегавајте опште дијалоге „Дошло је до грешке" без могућности опоравка.
Често постављана питања
Try-catch користи изузетке и одмотавање стека за обраду грешака, што може бити скупо у погледу перформанси при великом броју грешака. Result Type је тип-контејнер (Success или Failure) који се обрађује кроз pattern matching без одмотавања стека, што је ефикасније за очекиване грешке.
Finally је обавезан ако блок try отвара ресурсе (датотеке, сокете, курсоре) које треба затворити. Ако се ресурси не отварају, finally није потребан. У савременим језицима користите AutoCloseable/use/defer за аутоматско затварање ресурса без finally. use-блок у Kotlin и Swift замењује finally за Closeable објекте.
У нормалном току (без изузетка) try-catch практично не утиче на перформансе — JVM и Swift компајлер оптимизују овај случај. Али при бацању изузетка долази до одмотавања стека, што може трајати 10–100 μс у зависности од дубине стека. Не користите изузетке за управљање током извршења — то је антиобразац.
У корутинама користите 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. године. Саветоваћемо вас и предложити најбоље решење.
Прочитајте такође