はじめに
1.1 対象読者
- 対象:Swift または Kotlin のどちらかに慣れていて、もう一方へ“脳内スイッチ”したい iOS/Android/共通化エンジニア
- ゴール:型・宣言・非同期・エラー・ADT を最短経路で置き換えられるようにする
※ ADT = 抽象データ型 (Abstract Data Type、クラスとか構造体を指す)
1.2 本記事の読み方
- 2章の対応表で“あの書き方は Kotlin/Swift だと何?”を一気に変換
- その後、各章のサンプルを流し読み → 実務コードへコピペ&調整
クイック対応表
2.1 基本型マッピング(数値・文字列・真偽・コレクション)
概念 | Swift | Kotlin | 備考 |
---|---|---|---|
整数 | Int (64bit相当), UInt | Int (32bit), Long (64bit) | ビット幅に注意 |
実数 | Double , Float | Double , Float | 既定は両者とも Double が多い |
真偽 | Bool | Boolean | — |
文字 | Character | Char | — |
文字列 | String | String | — |
配列 | [T] / Array<T> | List<T> / MutableList<T> | Swift配列は値型、Kotlinはインターフェース+実装 |
辞書 | [K: V] / Dictionary | Map<K, V> / MutableMap<K, V> | — |
集合 | Set<T> | Set<T> / MutableSet<T> | — |
バイト列 | Data | ByteArray | — |
日付 | Date | Instant /LocalDateTime など | Java Time API を利用 |
任意型 | Any , AnyObject | Any | Kotlin の Any? は null 許容 |
コレクションの生成例:
// Swift
let list: [Int] = [1, 2, 3]
let dict: [String: Int] = ["a": 1]
var set: Set<String> = ["a", "b"]
// Kotlin
val list: List<Int> = listOf(1, 2, 3)
val dict: Map<String, Int> = mapOf("a" to 1)
val set: MutableSet<String> = mutableSetOf("a", "b")
2.2 値/参照・不変/可変(struct
/class
vs data class
/class
, let/var
vs val/var
)
Swift:
struct
は値型、class
は参照型。変数束縛はlet
(不変)/var
(可変)。let user = User(name: "A")
ならuser
は再代入不可。struct
のプロパティはuser
がlet
だと全体的に不変。
Kotlin:
data class
/class
は参照型。フィールドの不変はval
(読み取り専用)/var
(可変)で表現。data class
はequals/hashCode/copy
を自動生成。
例:
// Swift
struct User {
var name: String
}
var user1 = User(name: "Alice")
var user2 = user1
user2.name = "Bob" // user1のnameは変わらない
// Kotlin
data class User(
var name: String
)
val user1 = User("Alice")
val user2 = user1.copy(name = "Bob") // user1は変わらない
2.3 Null安全とオプショナル(T?
/if let
/guard
vs T?
/?.
/?:
/!!
)
Swift:
T?
が Optional。if let
/guard let
でアンラップ。?.
(チェーン)、??
(nil結合)。
Kotlin:
T?
が Nullable。?.
(セーフコール)、?:
(Elvis)、!!
(非推奨・実務では極力使わない)。
例:
// Swift
func greet(_ name: String?) -> String {
guard let name else { return "Hi, guest" }
return "Hi, \(name)"
}
let length = name?.count ?? 0
// Kotlin
fun greet(name: String?): String =
name?.let { "Hi, $it" } ?: "Hi, guest"
val length = name?.length ?: 0
2.4 列挙と代数的データ型(enum + associated values
↔ sealed class/interface
)
Swift:
- 関連値付き
enum
で ADT を自然に表現。switch
は網羅性チェックあり。
Kotlin:
sealed class
/sealed interface
とdata class/object
の組合せで ADT。when
で網羅性チェック(else
不要にできる)。
例:
// Swift
enum Result<T> {
case success(T)
case failure(Error)
}
func handle(_ r: Result<Int>) {
switch r {
case .success(let value):
print(value)
case .failure(let error):
print(error.localizedDescription)
}
}
// Kotlin
sealed interface Result<out T> {
data class Success<T>(val value: T): Result<T>
data class Failure(val error: Throwable): Result<Nothing>
}
fun handle(r: Result<Int>) = when (r) {
is Result.Success -> println(r.value)
is Result.Failure -> println(r.error.message)
}
2.5 エラーハンドリング(throws/try
↔ try/catch/Result
)
Swift:
- 投げる関数は
throws
、呼び出しはtry
。try?
でOptional
に変換、try!
はクラッシュ要因。do-catch
で捕捉。
Kotlin:
- チェック例外は存在せず、
try/catch
で捕捉。関数型合成や戻り値で扱いたい時はResult<T>
/runCatching
を使う。
例:
// Swift
enum MyError: Error {
case oops
}
func risky() throws -> Int {
throw MyError.oops
}
let a: Int? = try? risky() // nil
do {
_ = try risky()
} catch {
print(error)
}
// Kotlin
class MyError: RuntimeException("oops")
fun risky(): Int { throw MyError() }
val a: Result<Int> = runCatching { risky() }
a.getOrElse { 0 } // 0
try {
risky()
} catch (e: MyError) {
println(e)
}
2.6 非同期処理(async/await/Task
↔ suspend/Coroutines/Flow
)
Swift:
- 構造化並行性(
async/await
、Task
、async let
、TaskGroup
、Actor
)。キャンセルは協調的。
Kotlin:
suspend
関数+Coroutines(CoroutineScope
/launch
/async
/withContext
)とFlow
。同じく協調的キャンセル。
例:
// Swift
func fetchA() async throws -> Int { 1 }
func fetchB() async throws -> Int { 2 }
func fetchBoth() async throws -> Int {
async let a = fetchA()
async let b = fetchB()
return try await a + b // 3
}
// Kotlin
suspend fun fetchA(): Int = 1
suspend fun fetchB(): Int = 2
suspend fun fetchBoth(): Int = coroutineScope {
val a = async { fetchA() }
val b = async { fetchB() }
a.await() + b.await()
}
2.7 拡張と合成(Swift の extension
/プロトコル指向 ↔ Kotlin の拡張関数/インターフェース)
Swift:
extension
で後付けメソッド/プロトコル適合。プロトコル+デフォルト実装で合成。
Kotlin:
- 拡張関数/プロパティで後付け API。
interface
にデフォルト実装も可。両者とも静的ディスパッチ(実行時オーバーライドではない)に注意。
例:
// Swift
protocol Printable {
func text() -> String
}
extension Int: Printable {
func text() -> String { "\(self)" } // selfは、Int型
}
// Printableに準拠している要素を持つCollectionに対する拡張
extension Collection where Element: Printable {
func joinedText() -> String { map { $0.text() }.joined(separator: ",") }
}
// Kotlin
interface Printable {
fun text(): String
}
fun Int.text(): String = "$this"
fun <T: Printable> Collection<T>.joinedText(): String =
this.joinToString(",") {
it.text()
}