ThrowとThrowsは、プログラミング言語において例外をスローおよび宣言するためのメカニズムです。throw演算子は関数の通常の実行を中断し、エラーオブジェクトをコールスタックの上位に渡します。関数シグネチャのthrowsキーワードは、呼び出し側にエラーの可能性を警告し、コードを予測可能にします。Kotlinドキュメント(2026年)によると、Kotlinにおけるthrowは式(expression)であり、文(statement)ではないため、whenブロックやエルビス演算子内で使用できます。
ポイント
Throwは、プログラム実行の時点で例外を生成する演算子です。throwに遭遇すると、現在の実行フローは即座に中断され、制御はコールスタック内の最も近いcatchハンドラーに移譲されます。ハンドラーが見つからない場合、アプリケーションはクラッシュします。モバイル開発におけるthrowは、現在の抽象化レベルでは処理できないエラー(無効なサーバーレスポンス、ネットワークの不在、不正な引数など)を通知するために使用されます。
Throws — 関数シグネチャ内の修飾子(主にSwift)で、関数がエラーをスローする可能性があることを宣言します。これはSwiftにおけるcheckedエラーメカニズムの一部です。呼び出し側は、do-catch、try?、try!を介してエラーを処理するか、さらなる伝播のために自身の関数をthrowsとしてマークする必要があります。KotlinとJavaにもthrowsは存在しますが、Kotlinはそれを冗長とみなします。Kotlinのすべての例外はuncheckedであり、構文上の強制なしで処理されない可能性があります。Apple Swiftドキュメント(2026年)によると、Swiftのthrowsは関数型でエラーの可能性を明示的に宣言する唯一の方法であり、開発者にとってAPIコントラクトを透過的にします。
throwとthrowsの違いは基本的です。throwはアクション(実行時に例外をスローする)であり、throwsは宣言(コンパイル時のコントラクト)です。throwsのない関数はthrowを使用できません。Swiftコンパイラはエラーを生成します。throwsのある関数はthrowを使用しなくても構いません(許可されていますが無意味です)。この分離により、throw/throwsは強力なAPI設計ツールとなり、エラーコントラクトが関数の呼び出し前にシグネチャで可視化されます。
Swiftでは、throw演算子はErrorプロトコルに準拠する任意の型を受け入れます。最も一般的なのは、さまざまな種類のエラーに対応するケースを持つenumです。SwiftはJavaスタイルのchecked例外をサポートしていません。代わりに、関数型はthrowsでマークされ、処理は呼び出し側に委譲されます。これによりSwiftのthrowはより柔軟になりますが、開発者により多くの責任が生じます。
Errorプロトコルに準拠する任意の型は、throwを介してスローできます。開発者は通常、関連値なしのenum(単純なエラー用)または関連値あり(コンテキストを渡す用)を使用します。Swiftではエラーがenumである必要はありません。Errorを実装するstructやclassも使用できますが、ハンドラー側でのexhaustive switchによりenumが推奨されます。コンパイラは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は3つのシナリオ(無効な認証情報、期限切れトークン、残り分数を関連値として持つロックされたアカウント)を定義します。login関数はthrowsとして宣言されており、コンパイラはtryを介した呼び出しを要求します。関数内では、throwは無効なユーザー名とロックされたアカウントの2か所で使用されています。関連値accountLockedは、ユーザーに具体的なデータ(ロック解除まで待つ分数)を渡すことを可能にします。このアプローチにより、ロック状態を確認するための個別のAPIエンドポイントが不要になります。
KotlinではthrowはNothing型の式であり、文ではありません。つまり、代入の右辺、when式の中、エルビス演算子?:の中でthrowを使用できます。Nothing型はKotlinにおけるすべての型の特別なサブタイプであり、任意の型の値が必要な場所でthrowを使用できます。コンパイラはthrowの後に実行が継続されないことを理解し、このケースに対してブランチを要求しません。
NothingはKotlinのユニークな型で、すべての可能な型のサブタイプです。Nothingを返す関数(TODO()など)は正常に完了することはなく、常に例外をスローするか無限ループに入ります。これにより、throwは値が必要な場所(エルビス演算子、elseなしのwhen、変数初期化)での使用に自然な候補となります。throwがwhenブランチにある場合、コンパイラはそのブランチがNothingに至ることを理解し、そのブランチにreturnやelseを要求しません。
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")
}
}
}
最初の例では、エルビス演算子?:でthrowが使用されています。JSONにapiUrlフィールドがない場合、throw式は即座に実行を中断し、IllegalArgumentExceptionをスローします。Nothing型により、コンパイラは右辺の型をStringと推論できます(エルビスはStringを期待し、throwの型はNothing、NothingはStringのサブタイプです)。2番目の例では、when式内でthrowが使用されています。コードが既知のいずれにも一致しない場合、例外がスローされます。コンパイラはthrowの後のコードが到達不能であることを理解するため、関数の戻り値の型Stringは違反されません。
Swiftではthrowsはパラメータリストの後、戻り値の型の矢印の前に指定されます。throws付きの関数は、do-catch内またはtry?でのみ他のthrows関数を呼び出せます。throws関数がエラーを処理しない場合、呼び出し側に渡します。Swiftはrethrowsもサポートしています。これは、throwsクロージャを受け取りそのエラーを伝播する高階関数のための修飾子です。rethrowsは、渡されたクロージャがエラーをスローした場合にのみ関数がエラーをスローすることを意味し、関数自体はエラーを生成しません。
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
}
// throwsクロージャの使用
let parsed = try mapValues(jsonStrings) { str in
let data = Data(str.utf8)
return try JSONDecoder().decode(Item.self, from: data)
}
rethrowsにより、mapValues関数は柔軟になります。throwsクロージャと通常のクロージャの両方を受け入れます。throwsクロージャが渡された場合、mapValuesの呼び出しにはtryが必要です。通常の場合はtryは不要です。これにより、rethrowsはSwift標準ライブラリのmap、filter、reduceなどの高階関数に最適です。Appleの推奨:throwsクロージャを受け入れ、エラーの唯一のソースがそのクロージャであるAPIにはrethrowsを使用してください。関数が自身のエラーをスローできる場合はthrowsを使用してください。
Swiftのthrowsはchecked例外(Javaのように)に近く、スローされるエラーはシグネチャで宣言されます。KotlinとDartはunchecked例外を使用し、シグネチャでのthrowsは不要です。違いは基本的です。checkedは開発者にエラー処理を強制し(より安全だが冗長)、uncheckedは自由を与えるがエラー処理を忘れるリスクを高めます。Swiftはthrowsにcheckedを選択し、Kotlinはすべての例外にuncheckedを選択しました。両方のアプローチに利点があります。Swiftは言語レベルでより信頼性が高く、Kotlinはよりコンパクトで関数型変換チェーンに便利です。
モバイルアプリケーションでの構造化エラー処理には、基本のExceptionやErrorを使用する代わりに、カスタムエラー型を作成することをお勧めします。SwiftではErrorプロトコルを持つenum、KotlinではThrowable(またはException)を継承するsealed class、DartではExceptionを継承するclassが使用されます。カスタム型により、エラーをカテゴリごとにグループ化し、関連データを渡すことができます。
| 言語 | エラー型 | 特徴 |
|---|---|---|
| Swift | enum: Error { ... } | 関連値、catchでのexhaustive switch |
| Kotlin | sealed class : Throwable() | フィールド付きエラー用のData class、when式 |
| Dart | class implements Exception | Messageフィールド、catchのon句 |
| Java | class extends Exception | Checkedとunchecked、シグネチャでのthrows必須 |
カスタムエラーを設計する際は、1つのエラー=1つのシナリオのルールに従ってください。異なる原因をString messageフラグ付きの1つの型にまとめないでください。シナリオごとに個別のケース/サブクラスを作成します。これにより、呼び出し側は文字列比較ではなくパターンマッチング(when/switch)を介して各ケースを処理できます。Swiftではこれによりexhaustive checkingが提供され、NetworkError列挙型のケースが処理されていない場合、コンパイラが警告します。
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はThrowable(Kotlinの標準例外型)を継承します。各サブクラスは独自のフィールドを持つdata classです。Timeoutはミリ秒単位のタイムアウト時間、HttpErrorはコードとレスポンス本文、NoConnectionは元のIOExceptionを含みます。この設計により、各エラーを包括的なマッチングでwhenを介して処理できます(新しいサブクラスを追加すると、コンパイラがすべてのwhen式を更新するよう強制します)。
Swiftはthrows関数を呼び出す3つの方法を提供し、それぞれに独自の安全コントラクトがあります。tryは標準的な方法で、do-catchが必要か、throws関数内にいる必要があります。try?はエラーをnilに変換します。結果はオプショナルになり、エラー時にはnilを返し、型はTからT?に変わります。try!は処理なしの強制実行で、エラーがスローされるとアプリケーションはクラッシュします。try!はエラーが絶対に不可能な場合(明らかに有効なデータなど)にのみ使用してください。
let configPath = Bundle.main.path(forResource: "config", ofType: "json")!
// try? — オプショナルな結果
let data = try? Data(contentsOf: URL(fileURLWithPath: configPath))
let json = try? JSONSerialization.jsonObject(with: data ?? Data())
// try! — 保証された成功(確信がある場合のみ)
let decoder = JSONDecoder()
let defaultConfig = try! decoder.decode(
Config.self,
from: Config.defaultJSON
)
// try — 標準的な処理
do {
let user = try fetchUser()
showUser(user)
} catch let error as NetworkError {
showRetryAlert(error.message)
}
例では、try!はアプリバンドルに埋め込まれた明らかに有効なJSONに使用されています。正しいリリースではデコードエラーは不可能です。try?は設定ファイルの読み取りに使用されています。ファイルがないか破損している場合、アプリはクラッシュせずにデフォルト値を使用します。do-catch内のtryは、エラーが予想されユーザーの応答が必要なネットワークリクエストに使用されます。推奨:本番コードではtry!を避け、ビルド時に検証された定数データにのみ使用してください。
よくある質問
Throwはプログラム実行中にエラーをスローしてフローを中断する演算子です。Throwsは関数がエラーをスローする可能性があることを宣言する関数シグネチャ修飾子です。throwsのない関数はthrowを使用できません。throwsはコンパイル時のコントラクト、throwは実行時のアクションです。
Kotlinはunchecked例外の哲学に従っています。すべての例外は構文上の強制なしで未処理のままにできます。Kotlinの開発者は、Javaのthrowsが過剰なtry-catchブロックと空のcatchによるchecked例外の無視につながると考えています。KotlinのNothing型により、throwを式として使用でき、throwsをより柔軟なアプローチで置き換えます。
try!は、バンドルの明らかに有効なJSON、定数データ、正しいURLスキームなど、エラーが絶対に不可能な場合にのみ許容されます。本番コードでは、try!は例外でありルールではありません。try?はフォールバック値のあるオプショナルシナリオに適しており、必須のエラー処理にはtryとdo-catchを使用します。
Rethrowsはthrowsクロージャを受け取る関数の修飾子です。rethrows付きの関数は、渡されたクロージャがエラーをスローした場合にのみエラーをスローします。これにより、高階関数(map、filter)は呼び出し側にtryを強制することなく、throwsクロージャと非throwsクロージャの両方で動作できます。
はい、catch内でthrowを使用して、エラーを別の型にラップしたりコンテキストを追加してスタックの上位に伝播できます。これはエラーチェイニングまたは再スローと呼ばれます。Swiftではcatch内でさらにthrowするだけで十分です。Kotlinではcatchブロック内でthrowします。finallyブロックは制御がさらに渡される前に実行されます。
まとめ
ターンキー方式のモバイルアプリケーションを開発します
IT Sectrは2017年からスタートアップや企業向けにiOS・Androidアプリケーションを開発しています。私たちがご相談に乗り、最適なソリューションをご提案します。