Throw e Throws sono meccanismi per lanciare e dichiarare eccezioni nei linguaggi di programmazione. L'operatore throw interrompe l'esecuzione normale di una funzione e passa l'oggetto errore verso l'alto nello stack delle chiamate. La parola chiave throws nella firma di una funzione avvisa la parte chiamante della possibilità di un errore, rendendo il codice prevedibile. Secondo la Documentazione Kotlin (2026), throw in Kotlin è un'espressione, non un'istruzione, consentendo di utilizzarlo all'interno di blocchi when e dell'operatore Elvis.
Punti chiave
Throw è un operatore che genera un'eccezione nel punto di esecuzione del programma. Quando si incontra throw, il flusso di esecuzione corrente viene immediatamente interrotto e il controllo viene trasferito al gestore catch più vicino nello stack delle chiamate. Se non viene trovato alcun gestore, l'applicazione si blocca. Throw nello sviluppo mobile viene utilizzato per segnalare errori che non possono essere gestiti al livello di astrazione corrente — ad esempio, una risposta del server non valida, assenza di rete o argomenti errati.
Throws — un modificatore nella firma di una funzione (principalmente in Swift) che dichiara che la funzione può lanciare un errore. Questo fa parte del meccanismo degli errori checked in Swift: la parte chiamante è obbligata a gestire l'errore tramite do-catch, try?, try! o contrassegnare la propria funzione come throws per un'ulteriore propagazione. In Kotlin e Java throws esiste anche, ma Kotlin lo considera ridondante — tutte le eccezioni in Kotlin sono unchecked, il che significa che possono non essere gestite senza imposizione sintattica. Secondo la Documentazione Apple Swift (2026), throws in Swift è l'unico modo per dichiarare esplicitamente la possibilità di un errore nel tipo di funzione, rendendo i contratti API trasparenti per lo sviluppatore.
La differenza tra throw e throws è fondamentale: throw è un'azione (lanciare un'eccezione in esecuzione), throws è una dichiarazione (contratto in fase di compilazione). Una funzione senza throws non può usare throw — il compilatore Swift produrrà un errore. Una funzione con throws può non usare throw — è consentito ma senza significato. Questa separazione rende throw/throws un potente strumento di progettazione API, dove il contratto di errore è visibile nella firma della funzione prima della sua chiamata.
In Swift, l'operatore throw accetta qualsiasi tipo che implementi il protocollo Error. Più comunemente si tratta di un enum con casi per diversi tipi di errori. Swift non supporta le eccezioni checked in stile Java — invece, il tipo di funzione viene contrassegnato con throws e la gestione viene delegata alla parte chiamante. Questo rende throw in Swift più flessibile ma anche più responsabilità per lo sviluppatore.
Qualsiasi tipo conforme al protocollo Error può essere lanciato tramite throw. Gli sviluppatori usano più spesso un enum con casi senza valori associati (per errori semplici) o con valori associati (per passare contesto). Swift non richiede che l'errore sia un enum — puoi usare una struct o una classe che implementa Error, ma l'enum è preferibile grazie allo exhaustive switch dal lato del gestore. Il compilatore verifica che tutti i casi siano gestiti in 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 definisce tre scenari: credenziali non valide, token scaduto e account bloccato con un valore associato remainingMinutes. La funzione login è dichiarata come throws — il compilatore richiede di chiamarla tramite try. All'interno della funzione, throw viene utilizzato in due punti: per un nome utente non valido e per un account bloccato. Il valore associato accountLocked consente di passare dati concreti all'utente — quanti minuti attendere prima dello sblocco. Questo approccio elimina la necessità di endpoint API separati per verificare lo stato del blocco.
In Kotlin throw è un'espressione di tipo Nothing, non un'istruzione. Ciò significa che throw può essere utilizzato sul lato destro di un'assegnazione, all'interno di espressioni when e dell'operatore Elvis ?:. Il tipo Nothing è un sottotipo speciale di tutti i tipi in Kotlin, che consente di utilizzare throw in luoghi dove è richiesto un valore di qualsiasi tipo. Il compilatore comprende che l'esecuzione non continua dopo throw e non richiede un ramo per questo caso.
Nothing è un tipo unico in Kotlin che è un sottotipo di tutti i tipi possibili. Una funzione che restituisce Nothing (ad esempio, TODO()) non viene mai completata normalmente — o lancia sempre un'eccezione o entra in un ciclo infinito. Ciò rende throw un candidato naturale per l'uso in luoghi dove è richiesto un valore: operatore Elvis, when senza else, inizializzazione di variabili. Se throw si trova in un ramo when, il compilatore capisce che il ramo porta a Nothing e non richiede un return o else per quel ramo.
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")
}
}
}
Nel primo esempio, throw viene utilizzato nell'operatore Elvis ?:: se il campo apiUrl è assente dal JSON, l'espressione throw interrompe immediatamente l'esecuzione e lancia un'IllegalArgumentException. Il tipo Nothing consente al compilatore di dedurre il tipo del lato destro come String (Elvis si aspetta String, throw ha tipo Nothing, Nothing è un sottotipo di String). Nel secondo esempio, throw all'interno di un'espressione when: se il codice non corrisponde a nessun codice noto, viene lanciata un'eccezione. Il compilatore capisce che il codice dopo throw è irraggiungibile, quindi il tipo di ritorno della funzione String non viene violato.
In Swift throws viene specificato dopo l'elenco dei parametri e prima della freccia del tipo di ritorno. Una funzione con throws può chiamare solo altre funzioni throws all'interno di do-catch o con try?. Se una funzione throws non gestisce l'errore, lo passa alla parte chiamante. Swift supporta anche rethrows — un modificatore per funzioni di ordine superiore che accettano una closure throws e ne propagano l'errore. rethrows significa che la funzione lancia un errore solo se la closure passata ne ha lanciato uno — la funzione stessa non genera un errore.
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
}
// Utilizzo con closure throws
let parsed = try mapValues(jsonStrings) { str in
let data = Data(str.utf8)
return try JSONDecoder().decode(Item.self, from: data)
}
rethrows consente alla funzione mapValues di essere flessibile: accetta sia closure throws che normali. Se viene passata una closure throws, la chiamata a mapValues richiede try; se normale, try non è necessario. Ciò rende rethrows ideale per funzioni di ordine superiore come map, filter, reduce nella libreria standard di Swift. Raccomandazione Apple: usa rethrows per API che accettano closure throws e la cui unica fonte di errore è quella closure. Se la funzione può lanciare un proprio errore, usa throws.
Swift throws è più vicino alle eccezioni checked (come in Java) — gli errori lanciati vengono dichiarati nella firma. Kotlin e Dart utilizzano eccezioni unchecked — throws nella firma non è necessario. La differenza è fondamentale: checked obbliga lo sviluppatore a gestire l'errore (più sicuro ma più verboso), unchecked dà libertà ma aumenta il rischio di dimenticare di gestire un errore. Swift ha scelto checked per throws, Kotlin ha scelto unchecked per tutte le eccezioni. Entrambi gli approcci hanno vantaggi: Swift è più affidabile a livello di linguaggio, Kotlin è più compatto e comodo nelle catene di trasformazioni funzionali.
Per una gestione strutturata degli errori nelle applicazioni mobili, si consiglia di creare tipi di errore personalizzati invece di utilizzare Exception o Error di base. In Swift si utilizza un enum con il protocollo Error; in Kotlin, una sealed class che eredita da Throwable (o da Exception); in Dart, una classe che eredita da Exception. I tipi personalizzati consentono di raggruppare gli errori per categorie e passare dati associati.
| Linguaggio | Tipo di errore | Caratteristica |
|---|---|---|
| Swift | enum: Error { ... } | Valori associati, exhaustive switch in catch |
| Kotlin | sealed class : Throwable() | Data class per errori con campi, espressione when |
| Dart | class implements Exception | Campo Message, clausola on in catch |
| Java | class extends Exception | Checked vs unchecked, throws obbligatorio nella firma |
Quando si progettano errori personalizzati, segui la regola: un errore — uno scenario. Non combinare cause diverse in un unico tipo con un flag String message — crea casi/sottoclassi separati per ogni scenario. Ciò consentirà alla parte chiamante di gestire ogni caso tramite pattern matching (when/switch) anziché confronto di stringhe. In Swift ciò fornisce exhaustive checking — il compilatore avviserà se un caso dell'enum NetworkError non viene gestito.
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")
}
La sealed class NetworkError eredita da Throwable (il tipo standard di eccezione in Kotlin). Ogni sottoclasse è una data class con i propri campi: Timeout contiene la durata del timeout in millisecondi, HttpError contiene il codice e il corpo della risposta, NoConnection contiene l'IOException originale. Questo design consente di gestire ogni errore tramite when con corrispondenza esaustiva (quando aggiungi una nuova sottoclasse, il compilatore obbligherà ad aggiornare tutte le espressioni when).
Swift offre tre modi per chiamare funzioni throws, ciascuno con il proprio contratto di sicurezza. try è il modo standard: richiede do-catch o di trovarsi all'interno di una funzione throws. try? converte l'errore in nil — il risultato diventa opzionale, in caso di errore restituisce nil, il tipo cambia da T a T?. try! — esecuzione forzata senza gestione: se viene lanciato un errore, l'applicazione si blocca. Usa try! solo quando sei assolutamente certo che un errore sia impossibile (ad esempio, dati palesemente validi).
let configPath = Bundle.main.path(forResource: "config", ofType: "json")!
// try? — risultato opzionale
let data = try? Data(contentsOf: URL(fileURLWithPath: configPath))
let json = try? JSONSerialization.jsonObject(with: data ?? Data())
// try! — successo garantito (solo quando sicuri)
let decoder = JSONDecoder()
let defaultConfig = try! decoder.decode(
Config.self,
from: Config.defaultJSON
)
// try — gestione standard
do {
let user = try fetchUser()
showUser(user)
} catch let error as NetworkError {
showRetryAlert(error.message)
}
Nell'esempio, try! viene utilizzato per JSON palesemente valido incorporato nel bundle dell'app — l'errore di decodifica è impossibile con un rilascio corretto. try? viene utilizzato per leggere un file di configurazione — se il file è mancante o danneggiato, l'app utilizza valori predefiniti invece di bloccarsi. try in do-catch viene utilizzato per le richieste di rete dove un errore è previsto e richiede una risposta dell'utente. Raccomandazione: evita try! nel codice di produzione — usalo solo per dati costanti verificati al momento della compilazione.
Domande frequenti
Throw è un operatore che lancia un errore durante l'esecuzione del programma, interrompendo il flusso. Throws è un modificatore della firma della funzione che dichiara che la funzione può lanciare un errore. Una funzione senza throws non può usare throw. Throws è un contratto in fase di compilazione, throw è un'azione in fase di esecuzione.
Kotlin segue la filosofia delle eccezioni unchecked: tutte le eccezioni possono rimanere non gestite senza imposizione sintattica. Gli sviluppatori di Kotlin ritengono che throws in Java porti a blocchi try-catch eccessivi e all'ignoranza delle eccezioni checked tramite catch vuoti. Il tipo Nothing di Kotlin consente di utilizzare throw come espressione, sostituendo throws con un approccio più flessibile.
try! è accettabile solo quando sei assolutamente certo che un errore sia impossibile: JSON palesemente valido dal bundle, dati costanti, schemi URL corretti. Nel codice di produzione, try! è un'eccezione, non una regola. try? è preferibile per scenari opzionali con un valore di fallback, try con do-catch per la gestione obbligatoria degli errori.
Rethrows è un modificatore per funzioni che accettano closure throws. Una funzione con rethrows lancia un errore solo se la closure passata ne ha lanciato uno. Ciò consente alle funzioni di ordine superiore (map, filter) di funzionare sia con closure throws che non-throws senza forzare try sul lato chiamante.
Sì, all'interno di catch puoi usare throw per propagare l'errore verso l'alto nello stack, avvolgendolo in un tipo diverso o aggiungendo contesto. Questo si chiama error chaining o rethrow. In Swift, basta un altro throw all'interno di catch; in Kotlin, throw all'interno di un blocco catch. Il blocco finally viene eseguito prima che il controllo venga passato oltre.
Riepilogo
Svilupperemo un'applicazione mobile chiavi in mano
IT Sectr crea applicazioni iOS e Android per startup e aziende dal 2017. Ti consulteremo e ti proporremo la soluzione migliore.
Leggi anche