KotlinがSwiftより優れている10の点

# はじめに

SwiftとKotlinを使い始めて６年くらい経ちました。

よく似ているSwiftとKotlinですが、両方を使い続けるうちに、私の中では**総合点はKotlinの方が上**という印象が固まってきたので、優劣のあるポイントを紹介したいと思います。

# KotlinがSwiftより優れている10の点

## 1. Swiftは循環参照によるメモリリークが多発する

Swiftの一番嫌な点は、**循環参照するとメモリリークする**ことです。
特にクロージャー内で`self`を参照してメモリリークが起きることがよくあります。

対応としては、`weak`や`unowned`を書いて弱参照にするだけなんですが、初級者はほぼ抜けますし、理解していても、たまに書き忘れます。

XcodeやLintで機械的にチェックできればいいのですが、そういった方法も未だに確立されていません。
（頑張れば一応近いことはできるけど）

メモリリークの調査には結構時間がかかります。`[weak self]`を書き忘れただけで、そのようなメモリリークが起きてしまうのは大きなデメリットと言えます。

一方、Kotlinはガベージコレクションがいい感じにやってくれるので、循環参照しても何も問題はありません。

## 2.Swiftはクロージャー内でselfを書く必要がある

Swiftでは `@escaping` 指定されたクロージャー内でインスタンスメンバーにアクセスするとき`self.`を書く必要があります。

```swift:Swift
class Foo {
    var number = 0

    func example() {
        capture {
            // self.を書く必要がある
            print(self.number)
        }
    }
}

func capture(function: @escaping () -> Void) {}
```

頻繁にselfを書くことになるので、これが地味に手間です。
また、クロージャー内のコードを関数に切り出すと、今度は逆に`self.`が必要なくなるので、削除する必要があります。
（self.がついたままでも動くけど、いらないのでできれば消したい）

この仕様は、クロージャー内でselfを参照するとメモリリークを起こすことをプログラマーに意識させるためのものだと思いますが、もうちょっといい感じにして欲しいと思います。

### 追記
2023-03 Swift5.8で改善されました 🎉
`guard let self` をすると以降 `self` を省略できます。

```swift
var number = 0

func example() {
    capture { [weak self] in
        guard let self else { return }
        print(number)
    }
}
```

## 3.Kotlinの方が配列・マップの操作関数が豊富

Swiftにおける配列やディクショナリ、Kotlinにおけるリストやマップにおいて、**Kotlinの方が操作関数が豊富で使いやすい**印象があります。

例えば、Kotlinにはリストをマップに変換する機能がありますが、これはSwiftにはありません。

```kotlin:Kotlin
val list = listOf(
    Pair("A組", "田中"),
    Pair("A組", "鈴木"),
    Pair("B組", "伊藤"),
    Pair("B組", "佐藤"),
    Pair("C組", "高橋"),
)

val map = list.groupBy { it.first }
```

このコードで以下のように組みごとにグルーピングされたマップを作ることができます。

```
A組=[(A組, 田中), (A組, 鈴木)], 
B組=[(B組, 伊藤), (B組, 佐藤)], 
C組=[(C組, 高橋)]
```

### 追記

[`Dictionary(grouping:by:)`](https://developer.apple.com/documentation/swift/dictionary/init(grouping:by:))で似たことができますが、KotlinのgroupByがArrayの順序を保持するのに対し、こちらは順序を保持しないため、やや使い勝手が悪いです。


## 4.Kotlinは if や when が値を返せる

Kotlinでは**if文やwhen文が値を返す**ことができます。
ブロックの最後が自動で戻り値になるという仕様と組み合わせて、以下のようにしてif文の戻り値を直接変数に代入できます。

```kotlin:Kotlin
val result = if (condition) {
    "true"
} else {
    "false"
}
```

Swiftにはこの機能はないので、以下のようにやや冗長な書き方をせざるを得ません。

```swift:Swift
let result: String
if condition {
    result = "true"
} else {
    result = "false"
}
```

## 追記
Swift 5.9で、ifやswitchが値を返せるようになるようです。

https://github.com/apple/swift-evolution/blob/main/proposals/0380-if-switch-expressions.md

@zunda_pixel さん情報ありがとうございます。

## 5.Kotlinにはスコープ関数がある

Kotlinには以下のスコープ関数と呼ばれる、どのオブジェクトでも使える便利関数があるのですが、Swiftには同じようなものがありません。

- let
- with
- run
- apply
- also

ここでは詳細は説明しませんが、これらの関数はたまに便利です。


## 6.Swiftは文字列の切り出しがめんどい

Swiftは文字列から範囲を指定して部分文字列を切り出すのが面倒です。

以下は文字列 `text` の1〜3文字目を切り出すコードですが、Kotlinと比べるといかにSwiftが面倒かわかります。

```swift:Swift
let from = text.index(text.startIndex, offsetBy: 1)
let to = text.index(text.startIndex, offsetBy: 3)
let sub = text[from...to]
let result = String(sub)
```

```kotlin:Kotlin
val result = text.substring(1..3)
```

## 7.Kotlinは数値の計算で暗黙の型変換をしてくれる

Kotlinでは異なる型の数値でも、明示的な型変換なしで四則演算することができます。
例えば、IntとDoubleの値をそのまま + で合計することができます。

```kotlin:Kotlin
val a: Int = 1
val b: Double = 1.0
val result = a + b // そのまま足せる
```

一方Swiftにこの機能はなく、異なる型同士で計算する場合は、明示的に型を変換してやる必要があります。

```swift:Swift
let a: Int = 1
let b: Double = 1
let result = Double(a) + b // aをDoubleに変換する必要がある
```

Swiftの方が曖昧さがなくていいという意見もありそうですが、慣れてくるとやっぱり型変換を書かなくていいKotlinの方が便利です。


## 8.Swiftはオブジェクトの初期化中にインスタンスメンバーを使えない

Swiftのクラスや構造体は、オブジェクトの初期化中にインスタンス変数、インスタンス関数を使うことができません。

```swift:Swift
class Foo {
    var a = 1

    // ❌ 初期化中にインスタンス変数aにアクセスしているのでエラーになる
    var b = a 

    // ❌ 初期化中にインスタンス関数dにアクセスしているのでエラーになる
    var c = d()

    func d() -> Int { 0 }
}
```

`lazy` を使えば似たようなことはできますが、初期化中でもインスタンスメンバーを使えるKotlinと比べると面倒です。

## 9.Swiftはジェネリクスに制限が多い

Swiftはジェネリクスを用いたクラスやプロトコルにKotlinより制限が多く、ジェネリックスを使うとコンパイルエラーと格闘するハメになることが多いです。

例えば、Swiftにおけるジェネリクスには以下の制限があります。

- ジェネリッククラスはstaticプロパティを定義できない
- 任意の型パラメーターのジェネリッククラスの変数が作れない
- プロトコルの場合、ジェネリッククラスとは異なるAssociated Typeの形でジェネリクスを定義する必要がある
- Associated Typeもこれまた制限が多い

```swift:Swift
// ❌ プロトコルをジェネリクスにはできない
protocol Foo<T> {}
```

```swift:Swift
class Foo<T> {
    // ❌ ジェネリッククラスでstatic変数は定義できない
    static var a = 1
}
```

```swift:Swift
struct Store {
    // ❌ Tが任意のFoo型変数は作れない
    let foo: Foo<*>
}
```

Kotlinでは上記の制限はどれもありません。

## 10.Swiftはリフレクションが弱い

まあ、アプリの実装で使うことはほとんどないかもしれませんが、Kotlinと比べるとSwiftはリフレクションの機能が少なく、がっつりリフレクションを使った仕組みを作るのは難しそうです。

# 番外編：KotlinよりSwiftの方が優れているところ


Kotlinの優位な点ばかり挙げましたが、当然Swiftの方が優れている点もあるので、少し挙げておきます。

## Swiftは構造体を値渡しできる

Swiftの`struct`は引数や戻り値にした場合、コピーされ値渡しになりますが、Kotlinには同じ機能がありません。

Swiftのコードを全く同じようにKotlinに移植しても、この違いによってKotlin側だけバグっていたことが何度かありました。

## Swiftは`型名.`が型推論で省略できる

Swiftは型推論できる場合、`型名.`の型名の部分を省略することができます。

例えば、`Color`というenumを引数にとる、setColorという関数があった場合、Swiftでは　`Color.red` のColorの部分を省略して、`.red`のように書くことができます。

```swift:Swift
setColor(.red)
```

一方Kotlinはこの省略はできず、以下のようにColorから書く必要があります。

```kotlin:Kotlin
setColor(Color.red)
```

複数のenumを並べる場合などで、この省略ができるとだいぶコードをスッキリさせることができますし、enum以外でもたまに役に立つ時があります。

## Swiftはifやwhileの条件式を括弧で囲まなくていい

Kotlinはifやwhileの条件式を括弧で囲む必要がありますが、Swiftは括弧を書く必要がありません。

```Swift:swift
if condition {
    // コード
}
```

```Kotlin:kotlin
if (condition) {
    // コード
}
```

些細な違いではありますが、書かなくていいなら書かない方が楽です。

# おわりに

他にも違いは色々あると思いますが、特に気になる違いを挙げてみました。
全体的に、Kotlinの方が配慮があって人に優しい印象ですね。