Throw și Throws — mecanisme de aruncare și declarare a excepțiilor în limbaje de programare. Operatorul throw întrerupe execuția normală a funcției și transmite obiectul eroare în sus pe stiva de apeluri. Cuvântul cheie throws în semnătura funcției avertizează partea apelantă despre posibilitatea unei erori, făcând codul predictibil. Conform Kotlin Documentation (2026), throw în Kotlin este o expresie (expression), nu o instrucțiune, ceea ce permite utilizarea sa în interiorul blocurilor when și a operatorului elvis.
Principalele
Throw — este operatorul care generează o excepție în punctul de execuție a programului. Când întâlnește throw, fluxul curent de execuție este imediat întrerupt, iar controlul este transferat celui mai apropiat gestionar catch din stiva de apeluri. Dacă nu se găsește niciun gestionar, aplicația se termină anormal. Throw în dezvoltarea mobilă este folosit pentru semnalarea erorilor care nu pot fi gestionate la nivelul curent de abstractizare — de exemplu, răspuns incorect al serverului, lipsa rețelei sau argumente invalide.
Throws — modificator în semnătura funcției (în principal în Swift) care declară că funcția poate arunca o eroare. Aceasta face parte din mecanismul checked errors în Swift: partea apelantă este obligată să gestioneze eroarea prin do-catch, try?, try! sau să își marcheze propria funcție cu throws pentru transmitere ulterioară (propagation). În Kotlin și Java throws există de asemenea, dar Kotlin îl consideră redundant — toate excepțiile în Kotlin sunt unchecked, adică pot fi negestionate fără constrângere sintactică. Conform Apple Swift Documentation (2026), throws în Swift — este singura modalitate de a declara explicit posibilitatea unei erori în tipul funcției, ceea ce face contractele API transparente pentru dezvoltator.
Diferența dintre throw și throws este fundamentală: throw — este o acțiune (aruncarea excepției în runtime), throws — este o declarație (contract la compilare). O funcție fără throws nu poate folosi throw — compilatorul Swift va da o eroare. O funcție cu throws poate să nu folosească throw — acest lucru este permis, dar fără sens. Această separare face din throw/throws un instrument puternic de proiectare API, unde contractul de eroare este vizibil în semnătura funcției înainte de apelul acesteia.
În Swift operatorul throw acceptă orice tip care implementează protocolul Error. Cel mai adesea este un enum cu cazuri pentru diferite tipuri de erori. Swift nu suportă checked exceptions în stil Java — în schimb tipul funcției este marcat cu throws, iar gestionarea revine părții apelante. Acest lucru face throw în Swift mai flexibil, dar și mai responsabil pentru dezvoltator.
Orice tip care se conformează protocolului Error poate fi aruncat prin throw. Cel mai des dezvoltatorii folosesc enum cu cazuri fără valori asociate (pentru erori simple) sau cu associated values (pentru transmiterea contextului). Swift nu cere ca eroarea să fie enum — se poate folosi struct sau class care implementează Error, dar enum este preferat datorită exhaustive switch de partea gestionarului. Compilatorul verifică că toate cazurile sunt gestionate în 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 definește trei scenarii: credențiale incorecte, token expirat și cont blocat cu valoarea asociată remainingMinutes. Funcția login este declarată ca throws — compilatorul cere să fie apelată prin try. În interiorul funcției, throw este folosit în două locuri: la username invalid și la cont blocat. Valoarea asociată accountLocked permite transmiterea utilizatorului a unor date concrete — câte minute să aștepte până la deblocare. Această abordare elimină necesitatea unor endpoint-uri API separate pentru verificarea stării de blocare.
În Kotlin throw — este o expresie (expression) de tip Nothing, nu o instrucțiune. Aceasta înseamnă că throw poate fi folosit în partea dreaptă a atribuirii, în interiorul expresiilor when și al operatorului elvis ?:. Tipul Nothing — un subtip special al tuturor tipurilor în Kotlin, ceea ce permite utilizarea throw în locurile unde este necesară o valoare de orice tip. Compilatorul înțelege că după throw execuția nu continuă și nu necesită o ramură pentru acest caz.
Nothing — un tip unic în Kotlin care este subtipul tuturor tipurilor posibile. O funcție care returnează Nothing (de exemplu TODO()) nu se termină niciodată normal — fie aruncă întotdeauna o excepție, fie intră într-o buclă infinită. Aceasta face din throw un candidat natural pentru utilizarea în locurile unde este necesară o valoare: operatorul elvis, when fără else, inițializarea variabilelor. Dacă throw se află într-o ramură when, compilatorul înțelege că ramura duce la Nothing și nu necesită return sau else pentru această ramură.
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")
}
}
}
În primul exemplu throw este folosit în operatorul elvis ?:: dacă câmpul apiUrl lipsește din JSON, expresia throw întrerupe imediat execuția și aruncă IllegalArgumentException. Tipul Nothing permite compilatorului să deducă tipul părții drepte ca String (elvis așteaptă String, throw are tipul Nothing, Nothing este subtipul String). În al doilea exemplu throw în interiorul expresiei when: dacă codul nu corespunde niciunuia dintre cele cunoscute, se aruncă o excepție. Compilatorul înțelege că după throw codul este inaccesibil, deci tipul returnat al funcției String nu este încălcat.
În Swift throws este specificat după lista de parametri și înainte de săgeata tipului returnat. O funcție cu throws poate apela doar alte funcții throws în interiorul do-catch sau cu try?. Dacă o funcție throws nu gestionează eroarea, o transmite părții apelante. Swift suportă de asemenea rethrows — un modificator pentru funcțiile de ordin superior care acceptă un closure cu throws și propagă eroarea acestuia. Rethrows înseamnă că funcția aruncă o eroare doar dacă closure-ul transmis a aruncat-o — funcția însăși nu generează eroarea.
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
}
// Utilizarea cu closure-ul throws
let parsed = try mapValues(jsonStrings) { str in
let data = Data(str.utf8)
return try JSONDecoder().decode(Item.self, from: data)
}
Rethrows permite funcției mapValues să fie flexibilă: acceptă atât closure-uri throws, cât și obișnuite. Dacă este transmis un closure throws, apelul mapValues necesită try; dacă este obișnuit — try nu este necesar. Aceasta face rethrows ideal pentru funcțiile de ordin superior precum map, filter, reduce din biblioteca standard Swift. Recomandarea Apple: folosiți rethrows pentru API-uri care acceptă closure-uri throws și singura sursă de eroare este acest closure. Dacă funcția poate arunca propria eroare, folosiți throws.
Swift throws este mai aproape de checked exceptions (ca în Java) — erorile aruncate sunt declarate în semnătură. Kotlin și Dart folosesc excepții unchecked — throws în semnătură nu este necesar. Diferența este fundamentală: checked obligă dezvoltatorul la gestionare (mai sigur, dar mai verbose), unchecked oferă libertate, dar crește riscul de a uita să gestionezi eroarea. Swift a ales checked pentru throws, Kotlin — unchecked pentru toate excepțiile. Ambele abordări au avantaje: Swift este mai fiabil la nivel de limbaj, Kotlin este mai compact și mai comod în lanțurile de transformări funcționale.
Pentru gestionarea structurată a erorilor în aplicațiile mobile se recomandă crearea propriilor tipuri de erori în locul utilizării Exception sau Error de bază. În Swift pentru aceasta se folosește enum cu protocolul Error, în Kotlin — sealed class cu moștenirea de la Throwable (sau de la Exception), în Dart — class cu moștenirea de la Exception. Tipurile personalizate permit gruparea erorilor pe categorii și transmiterea datelor asociate.
| Limbaj | Tip eroare | Caracteristică |
|---|---|---|
| Swift | enum: Error { ... } | Valori asociate, exhaustive switch în catch |
| Kotlin | sealed class : Throwable() | Data class pentru erori cu câmpuri, expresia when |
| Dart | class implements Exception | Câmp Message, clauza on în catch |
| Java | class extends Exception | Checked vs unchecked, throws obligatoriu în semnătură |
La proiectarea erorilor personalizate respectați regula: o eroare — un scenariu. Nu combinați diferite cauze într-un singur tip cu un flag String message — creați cazuri/subclase separate pentru fiecare scenariu. Acest lucru va permite părții apelante să gestioneze fiecare caz prin pattern matching (when/switch), nu prin compararea șirurilor. În Swift aceasta oferă exhaustive checking — compilatorul va avertiza dacă un caz al enum NetworkError nu a fost gestionat.
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 moștenește din Throwable (tipul standard de excepții în Kotlin). Fiecare subclasă este o data class cu propriile câmpuri: Timeout conține durata timeout-ului în milisecunde, HttpError — codul și corpul răspunsului, NoConnection — IOException-ul original. Un astfel de design permite gestionarea fiecărei erori prin when cu parcurgere exhaustivă (când adăugați o nouă subclasă, compilatorul vă va forța să actualizați toate expresiile when).
Swift oferă trei variante de apelare a funcțiilor throws, fiecare cu propriul contract de securitate. try — modul standard: necesită do-catch sau aflarea în interiorul unei funcții throws. try? transformă eroarea în nil — rezultatul devine opțional, la eroare se returnează nil, tipul se schimbă din T în T?. try! — executare forțată fără gestionare: dacă eroarea este aruncată, aplicația se prăbușește. Folosiți try! doar când sunteți absolut sigur că eroarea este imposibilă (de exemplu, date evident corecte).
let configPath = Bundle.main.path(forResource: "config", ofType: "json")!
// try? — rezultat opțional
let data = try? Data(contentsOf: URL(fileURLWithPath: configPath))
let json = try? JSONSerialization.jsonObject(with: data ?? Data())
// try! — succes garantat (doar când sunteți sigur)
let decoder = JSONDecoder()
let defaultConfig = try! decoder.decode(
Config.self,
from: Config.defaultJSON
)
// try — gestionare standard
do {
let user = try fetchUser()
showUser(user)
} catch let error as NetworkError {
showRetryAlert(error.message)
}
În exemplu try! este folosit pentru JSON-ul evident valid, încorporat în bundle-ul aplicației — eroarea de decodare este imposibilă la o lansare corectă. try? este aplicat pentru citirea unui fișier de configurare — dacă fișierul lipsește sau este deteriorat, aplicația folosește valorile implicite, nu se prăbușește. try în do-catch este folosit pentru cererile de rețea, unde eroarea este așteptată și necesită reacția utilizatorului. Recomandare: evitați try! în codul de producție — faceți excepție doar pentru date constante, verificate în etapa de compilare.
Întrebări frecvente
Throw — operator care aruncă o eroare în timpul execuției programului, întrerupând fluxul. Throws — modificator al semnăturii funcției care declară că funcția poate arunca o eroare. O funcție fără throws nu poate folosi throw. Throws este un contract la compilare, throw este o acțiune în runtime.
Kotlin urmează filosofia unchecked exceptions: toate excepțiile pot fi negestionate fără constrângere sintactică. Dezvoltatorii Kotlin consideră că throws în Java duce la blocuri try-catch redundante și ignorarea checked exceptions prin catch-uri goale. Tipul Nothing din Kotlin permite utilizarea throw ca expresie, înlocuind throws într-un mod mai flexibil.
try! este permis doar când sunteți absolut sigur că eroarea este imposibilă: JSON evident valid din bundle, date constante, scheme URL corecte. În codul de producție try! este o excepție, nu o regulă. try? este preferat pentru scenarii opționale cu valoare implicită, try cu do-catch — pentru gestionarea obligatorie a erorilor.
Rethrows — modificator pentru funcțiile care acceptă closure-uri throws. O funcție cu rethrows aruncă o eroare doar dacă closure-ul transmis a aruncat-o. Acest lucru permite funcțiilor de ordin superior (map, filter) să lucreze atât cu closure-uri throws, cât și non-throws fără try forțat de partea apelantă.
Da, în interiorul catch se poate folosi throw pentru a propaga eroarea mai sus pe stivă, împachetând-o într-un alt tip sau adăugând context. Aceasta se numește error chaining sau rethrow. În Swift este suficient un alt throw în interiorul catch, în Kotlin — throw în interiorul blocului catch. Blocul finally se va executa înainte de transmiterea controlului mai departe.
Rezumat
Vom dezvolta o aplicație mobilă la cheie
IT Sectr creează aplicații iOS și Android pentru startup-uri și afaceri din 2017. Vă vom consilia și vă vom propune cea mai bună soluție.
Citiți și