Throw/Throws — o que é, operadores de lançamento de exceções e como funcionam no desenvolvimento móvel

Autor: IT Sectr Publicado: 2026-05-26 Tempo de leitura: 9 min

Throw e Throws são mecanismos para lançar e declarar exceções em linguagens de programação. O operador throw interrompe a execução normal de uma função e passa o objeto de erro para cima na pilha de chamadas. A palavra-chave throws na assinatura da função avisa a parte chamadora sobre a possibilidade de um erro, tornando o código previsível. De acordo com a Documentação do Kotlin (2026), throw em Kotlin é uma expressão, não uma instrução, o que permite usá-lo dentro de blocos when e do operador Elvis.

Pontos principais

  • Throw — um operador que interrompe a execução da função e passa o objeto de erro para cima na pilha de chamadas
  • Throws — uma palavra-chave na assinatura da função que declara que a função pode lançar uma exceção
  • Em Swift a função é marcada com throws, a chamada requer try/try?/try!, e throw aceita qualquer tipo que implemente o protocolo Error
  • Em Kotlin throw é uma expressão do tipo Nothing, throws está ausente (todas as exceções são unchecked)
  • Erros personalizados são criados via enum (Swift) ou sealed class (Kotlin) para tratamento conveniente no catch

O que é Throw e Throws?

Throw é um operador que gera uma exceção no ponto de execução do programa. Quando throw é encontrado, o fluxo de execução atual é imediatamente interrompido e o controle é transferido para o manipulador catch mais próximo na pilha de chamadas. Se nenhum manipulador for encontrado, a aplicação falha. Throw no desenvolvimento móvel é usado para sinalizar erros que não podem ser tratados no nível atual de abstração — por exemplo, uma resposta de servidor inválida, ausência de rede ou argumentos incorretos.

Throws — um modificador na assinatura de uma função (principalmente em Swift) que declara que a função pode lançar um erro. Isso faz parte do mecanismo de erros checked em Swift: a parte chamadora é obrigada a tratar o erro através de do-catch, try?, try! ou marcar sua própria função como throws para propagação posterior. Em Kotlin e Java throws também existe, mas Kotlin o considera redundante — todas as exceções em Kotlin são unchecked, ou seja, podem não ser tratadas sem imposição sintática. De acordo com a Documentação da Apple Swift (2026), throws em Swift é a única forma de declarar explicitamente a possibilidade de um erro no tipo da função, tornando os contratos de API transparentes para o desenvolvedor.

A diferença entre throw e throws é fundamental: throw é uma ação (lançar uma exceção em tempo de execução), throws é uma declaração (contrato em tempo de compilação). Uma função sem throws não pode usar throw — o compilador Swift produzirá um erro. Uma função com throws pode não usar throw — é permitido mas sem sentido. Essa separação torna throw/throws uma ferramenta poderosa de design de API, onde o contrato de erro é visível na assinatura da função antes de sua chamada.

Throw em Swift

Em Swift, o operador throw aceita qualquer tipo que implemente o protocolo Error. Mais comumente é um enum com casos para diferentes tipos de erros. Swift não suporta exceções checked no estilo Java — em vez disso, o tipo da função é marcado com throws e o tratamento é delegado à parte chamadora. Isso torna throw em Swift mais flexível mas também mais responsabilidade para o desenvolvedor.

Protocolo Error e tipos de erro

Qualquer tipo que esteja em conformidade com o protocolo Error pode ser lançado via throw. Os desenvolvedores costumam usar um enum com casos sem valores associados (para erros simples) ou com valores associados (para passar contexto). Swift não exige que o erro seja um enum — você pode usar um struct ou class implementando Error, mas o enum é preferível graças ao exhaustive switch no lado do manipulador. O compilador verifica se todos os casos são tratados no do-catch.

swift
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 três cenários: credenciais inválidas, token expirado e conta bloqueada com um valor associado de remainingMinutes. A função login é declarada como throws — o compilador exige chamá-la via try. Dentro da função, throw é usado em dois lugares: para um nome de usuário inválido e para uma conta bloqueada. O valor associado accountLocked permite passar dados concretos ao usuário — quantos minutos esperar antes do desbloqueio. Essa abordagem elimina a necessidade de endpoints de API separados para verificar o status do bloqueio.

Throw em Kotlin

Em Kotlin throw é uma expressão do tipo Nothing, não uma instrução. Isso significa que throw pode ser usado no lado direito de uma atribuição, dentro de expressões when e do operador Elvis ?:. O tipo Nothing é um subtipo especial de todos os tipos em Kotlin, o que permite usar throw em lugares onde um valor de qualquer tipo é necessário. O compilador entende que a execução não continua após throw e não requer um ramo para este caso.

Tipo Nothing e seu papel na composição

Nothing é um tipo único em Kotlin que é um subtipo de todos os tipos possíveis. Uma função que retorna Nothing (por exemplo, TODO()) nunca se completa normalmente — ou sempre lança uma exceção ou entra em um loop infinito. Isso torna throw um candidato natural para uso em lugares onde um valor é necessário: operador Elvis, when sem else, inicialização de variáveis. Se throw está em um ramo when, o compilador entende que o ramo leva a Nothing e não requer um return ou else para esse ramo.

kotlin
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")
        }
    }
}

No primeiro exemplo, throw é usado no operador Elvis ?:: se o campo apiUrl está ausente do JSON, a expressão throw interrompe imediatamente a execução e lança uma IllegalArgumentException. O tipo Nothing permite ao compilador inferir o tipo do lado direito como String (Elvis espera String, throw tem tipo Nothing, Nothing é um subtipo de String). No segundo exemplo, throw dentro de uma expressão when: se o código não corresponde a nenhum conhecido, uma exceção é lançada. O compilador entende que o código após throw é inatingível, portanto o tipo de retorno da função String não é violado.

Throws na assinatura da função e rethrows

Em Swift throws é especificado após a lista de parâmetros e antes da seta do tipo de retorno. Uma função com throws só pode chamar outras funções throws dentro de do-catch ou com try?. Se uma função throws não trata o erro, ela o passa para a parte chamadora. Swift também suporta rethrows — um modificador para funções de ordem superior que aceitam um closure throws e propagam seu erro. rethrows significa que a função só lança um erro se o closure passado lançou um — a função em si não gera um erro.

swift
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
}

// Uso com closure throws
let parsed = try mapValues(jsonStrings) { str in
    let data = Data(str.utf8)
    return try JSONDecoder().decode(Item.self, from: data)
}

rethrows permite que a função mapValues seja flexível: ela aceita tanto closures throws quanto normais. Se um closure throws é passado, a chamada mapValues requer try; se normal, try não é necessário. Isso torna rethrows ideal para funções de ordem superior como map, filter, reduce na biblioteca padrão do Swift. Recomendação da Apple: use rethrows para APIs que aceitam closures throws e a única fonte de erro é esse closure. Se a função pode lançar seu próprio erro, use throws.

Exceções checked vs unchecked

Swift throws está mais próximo das exceções checked (como em Java) — os erros lançados são declarados na assinatura. Kotlin e Dart usam exceções unchecked — throws na assinatura não é necessário. A diferença é fundamental: checked obriga o desenvolvedor a tratar o erro (mais seguro mas mais verboso), unchecked dá liberdade mas aumenta o risco de esquecer de tratar um erro. Swift escolheu checked para throws, Kotlin escolheu unchecked para todas as exceções. Ambas as abordagens têm vantagens: Swift é mais confiável no nível da linguagem, Kotlin é mais compacto e conveniente em cadeias de transformações funcionais.

Tipos de erro personalizados

Para tratamento estruturado de erros em aplicativos móveis, recomenda-se criar tipos de erro personalizados em vez de usar Exception ou Error base. Em Swift usa-se um enum com o protocolo Error; em Kotlin, uma sealed class herdando de Throwable (ou de Exception); em Dart, uma classe herdando de Exception. Tipos personalizados permitem agrupar erros por categorias e passar dados associados.

LinguagemTipo de erroCaracterística
Swiftenum: Error { ... }Valores associados, exhaustive switch no catch
Kotlinsealed class : Throwable()Data class para erros com campos, expressão when
Dartclass implements ExceptionCampo Message, cláusula on no catch
Javaclass extends ExceptionChecked vs unchecked, throws obrigatório na assinatura

Ao projetar erros personalizados, siga a regra: um erro — um cenário. Não combine diferentes causas em um único tipo com uma flag String message — crie casos/subclasses separados para cada cenário. Isso permitirá que a parte chamadora trate cada caso via pattern matching (when/switch) em vez de comparação de strings. Em Swift isso fornece exhaustive checking — o compilador avisará se algum caso do enum NetworkError não for tratado.

Exemplo de erro personalizado em Kotlin

kotlin
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")
}

A sealed class NetworkError herda de Throwable (o tipo padrão de exceção em Kotlin). Cada subclasse é uma data class com seus próprios campos: Timeout contém a duração do tempo limite em milissegundos, HttpError contém o código e o corpo da resposta, NoConnection contém a IOException original. Esse design permite tratar cada erro via when com correspondência exaustiva (quando você adiciona uma nova subclasse, o compilador forçará a atualização de todas as expressões when).

Try, try? e try! em Swift

Swift oferece três formas de chamar funções throws, cada uma com seu próprio contrato de segurança. try é a forma padrão: requer do-catch ou estar dentro de uma função throws. try? converte o erro em nil — o resultado se torna opcional, em caso de erro retorna nil, o tipo muda de T para T?. try! — execução forçada sem tratamento: se um erro é lançado, a aplicação falha. Use try! apenas quando estiver absolutamente certo de que um erro é impossível (por exemplo, dados obviamente válidos).

swift
let configPath = Bundle.main.path(forResource: "config", ofType: "json")!

// try? — resultado opcional
let data = try? Data(contentsOf: URL(fileURLWithPath: configPath))
let json = try? JSONSerialization.jsonObject(with: data ?? Data())

// try! — sucesso garantido (apenas quando tiver certeza)
let decoder = JSONDecoder()
let defaultConfig = try! decoder.decode(
    Config.self,
    from: Config.defaultJSON
)

// try — tratamento padrão
do {
    let user = try fetchUser()
    showUser(user)
} catch let error as NetworkError {
    showRetryAlert(error.message)
}

No exemplo, try! é usado para JSON obviamente válido embutido no bundle do aplicativo — erro de decodificação é impossível com um lançamento correto. try? é usado para ler um arquivo de configuração — se o arquivo estiver ausente ou corrompido, o aplicativo usa valores padrão em vez de falhar. try em do-catch é usado para requisições de rede onde um erro é esperado e requer resposta do usuário. Recomendação: evite try! em código de produção — use apenas para dados constantes verificados em tempo de compilação.

Perguntas frequentes

Qual é a diferença entre throw e throws em Swift?

Throw é um operador que lança um erro durante a execução do programa, interrompendo o fluxo. Throws é um modificador da assinatura da função que declara que a função pode lançar um erro. Uma função sem throws não pode usar throw. Throws é um contrato em tempo de compilação, throw é uma ação em tempo de execução.

Por que não há throws em Kotlin?

Kotlin segue a filosofia de exceções unchecked: todas as exceções podem não ser tratadas sem imposição sintática. Os desenvolvedores do Kotlin consideram que throws em Java leva a blocos try-catch excessivos e à ignorância de exceções checked via catch vazios. O tipo Nothing do Kotlin permite usar throw como expressão, substituindo throws por uma abordagem mais flexível.

Quando usar try! em Swift?

try! só é aceitável quando você está absolutamente certo de que um erro é impossível: JSON obviamente válido do bundle, dados constantes, esquemas de URL corretos. Em código de produção, try! é uma exceção, não uma regra. try? é preferível para cenários opcionais com um valor de fallback, try com do-catch para tratamento obrigatório de erros.

O que é rethrows em Swift?

Rethrows é um modificador para funções que aceitam closures throws. Uma função com rethrows só lança um erro se o closure passado lançou um. Isso permite que funções de ordem superior (map, filter) trabalhem com closures throws e não-throws sem forçar try na parte chamadora.

Pode-se lançar um erro dentro de um bloco catch?

Sim, dentro de catch você pode usar throw para propagar o erro para cima na pilha, envolvendo-o em um tipo diferente ou adicionando contexto. Isso é chamado de error chaining ou rethrow. Em Swift, basta outro throw dentro de catch; em Kotlin, throw dentro de um bloco catch. O bloco finally é executado antes que o controle seja passado adiante.

Resumo

  • Throw — um operador de lançamento de exceção que interrompe o fluxo atual e passa o objeto de erro para cima na pilha
  • Throws — uma declaração em tempo de compilação na assinatura da função sobre a possibilidade de um erro, obrigatório em Swift, ausente em Kotlin
  • Em Swift throw aceita enum: Error, a função é marcada com throws, chamada via try / try? / try!
  • Em Kotlin throw é uma expressão do tipo Nothing, permitindo seu uso em when, operador Elvis e atribuições
  • Rethrows em Swift permite que funções de ordem superior propaguem erros apenas dos closures passados
  • Erros personalizados são criados via enum (Swift) ou sealed class (Kotlin) com valores associados para informação detalhada
  • Projete tipos de erro no princípio "um caso — um cenário" para tratamento conveniente via pattern matching

Vamos desenvolver um aplicativo móvel chave na mão

A IT Sectr cria aplicativos para iOS e Android para startups e empresas desde 2017. Nós vamos aconselhá-lo e propor a melhor solução.

Discutir o projeto

Leia também