40
18

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

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

Last updated at Posted at 2023-08-10

はじめに

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

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

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

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

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

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

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

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

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

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

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

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 を省略できます。

var number = 0

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

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

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

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

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:)で似たことができますが、KotlinのgroupByがArrayの順序を保持するのに対し、こちらは順序を保持しないため、やや使い勝手が悪いです。

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

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

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

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

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

追記

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

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

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

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

  • let
  • with
  • run
  • apply
  • also

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

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

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

以下は文字列 text の1〜3文字目を切り出すコードですが、Kotlinと比べるといかに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
val result = text.substring(1..3)

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

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

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

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

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

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

8.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
// ❌ プロトコルをジェネリクスにはできない
protocol Foo<T> {}
Swift
class Foo<T> {
    // ❌ ジェネリッククラスでstatic変数は定義できない
    static var a = 1
}
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
setColor(.red)

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

Kotlin
setColor(Color.red)

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

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

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

swift
if condition {
    // コード
}
kotlin
if (condition) {
    // コード
}

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

おわりに

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

40
18
12

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
40
18

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?