ソフトウェア設計

Swiftのfinalキーワードをクラスに付ける本当の理由(パフォーマンス・設計まとめ)

コードレビューで「このクラスにfinalを付けてください」と言われたことが、一度はあるのではないでしょうか。

読了 5 分
Swiftのfinalキーワードをクラスに付ける本当の理由(パフォーマンス・設計まとめ)のカバー画像

コードレビューで「このクラスにfinalを付けてください」と言われたことが、一度はあるのではないでしょうか。

単なる習慣に見えますが、理由を知るとコードの見方が変わります。

結論から言うと、finalは「このクラスを継承によって拡張するつもりはない」という宣言です。パフォーマンスと設計の安全性を同時に高める、意外に重みのある一行です。

今回は、Swiftのクラスにfinalを付ける本当の理由を整理します。


finalとは何ですか?

finalは継承とオーバーライドを禁止するキーワードです。

クラスの前に付けると、ほかのクラスがそのクラスを継承できなくなります。

メソッドやプロパティの前に付ければ、そのメンバーだけオーバーライドを禁止できます。

final class Logger {
    func log(_ msg: String) { print("[LOG] \(msg)") }
}

let logger = Logger()
logger.log("支払い完了")   // 出力: [LOG] 支払い完了

Loggerを継承するためにclass FileLogger: Loggerと書くと、コンパイルエラーになります。

「このクラスはここで終わりです」と釘を刺すわけです。


finalを付けるとなぜ速くなるのでしょうか?

一つ目の理由はパフォーマンスです。ポイントはディスパッチ方式にあります。

継承の可能性が残っていると、コンパイラーは実際に呼ばれるメソッドをランタイムまで判断できないため、毎回テーブルを調べて探す必要があります。

これを動的ディスパッチと呼びます。クラスごとのメソッドテーブル(vtable)を参照してから呼び出します。

一方、finalが付くと状況は変わります。

子クラスが割り込めないため、コンパイラーは「この呼び出しは必ずこのメソッドだ」と確信できます。

そのためテーブルを探さず、直接呼び出せます。これが静的ディスパッチです。

さらに、短いメソッドなら呼び出し箇所にコードを展開するインライン化も可能になります。

一回の呼び出しコストは大きくありませんが、ループ内で何千回も呼ばれるメソッドなら差が積み重なります。

一つの呼び出しが、この分岐で静的か動的かに分かれます
一つの呼び出しが、この分岐で静的か動的かに分かれます

本当の理由はパフォーマンスより「設計」です

実際、私はパフォーマンスよりこちらのほうが重要だと思います。

脆弱な基底クラス問題(fragile base class)を防げるからです。

継承を開いておくと、自分が作った親クラスの内部動作に誰でも自由に介入できます。

親のメソッドを何気なく一つ変更しただけで、それをオーバーライドした子クラスが次々に壊れることがあります。

継承はカプセル化を最も大きく壊す関係です。子クラスが親の内部まで見られるようになるからです。

そのため、オブジェクト指向設計の原則でもこう言われます。

継承のために設計し、ドキュメント化せよ。そうでなければ継承を禁止せよ。—Effective Javaの有名な助言です。

finalは、この助言をコードで実践するためのツールです。

「継承用に設計していないので、開いておかない」という意図を、コンパイラーが強制してくれます。

意図が明確になれば、後からコードを読む同僚も迷いにくくなります。

開く必要がなければfinal。意図をコンパイラーに守らせます
開く必要がなければfinal。意図をコンパイラーに守らせます

では、必ず付けるべきなのでしょうか?

ここで重要な話です。Swiftでは、参照型は必ずしもクラスである必要はありません。

状態を値として扱えるなら、structのほうが適している場合が多くあります。structにはそもそも継承がありません。

つまり「finalを付けるか」を考える前に、「これは本当にクラスである必要があるか」を先に問うべきです。

それでもクラスを使う必要があるなら、次の基準で判断してください。

状況 判断
継承を想定していないクラス finalを付ける(デフォルト)
パフォーマンスが重要な繰り返し呼び出しコード finalで静的ディスパッチを促す
明確な拡張ポイントを設計したフレームワーク finalを付けず開いておく
UIViewControllerなど継承を前提とするAPI 付けない

まとめると、次のように覚えると簡単です。

  • 継承を意図していないなら、基本的にfinalを付ける
  • 拡張ポイントは、開きたい場所だけを選んで開く
  • structで済むなら、そもそもクラスを避ける

面接ではこう質問されます

Q. finalキーワードを付けると、どのようなメリットがありますか?

継承とオーバーライドを防ぎ、設計意図を明確に示せます。同時に、コンパイラーは動的ディスパッチの代わりに静的ディスパッチを使えるため、呼び出しコストが下がり、インライン化の最適化も可能になります。

Q. では、すべてのクラスにfinalを付けるべきですか?

継承のために設計し、ドキュメント化したクラスは開いておくべきです。ただし、その意図がないクラスは基本的に閉じておくほうが安全で、値として扱う意味が合うなら、まずstructを検討するのがよいでしょう。


finalの一行は単なる最適化のヒントではなく、「このクラスをどう使うのか」への答えです。

今日からクラスを新しく作るときは、「継承を開いておく理由はあるか」と一度問いかけてみてください。その習慣だけで、コードはずっと堅牢になります。

あわせて読みたい