Swift 3でクラスやメソッドにつける public
や private
などのアクセスコントロールが見直されました。新しいキーワードが増えただけでなく、これまでのキーワードの意味も変わったので、何がどう変わったのか、それらをどう使い分けていくのかというところを自分なりにまとめました。
これまでのアクセスコントロール
Swift 2までは次の3つのアクセスレベルがありました。何もつけなければ internal
が指定されたことになります。
-
public
: 同じモジュール内だけでなく、別のモジュールからでもアクセスできる -
internal
: 同じモジュール内からのみアクセスできる -
private
: 同じソースファイル内からのみアクセスできる
Swift 3からのアクセスコントロール
Swift 3からは次の5つのアクセスレベルになります。何も付けなければ internal
になる点はこれまでと同じです。
-
open
: 同じモジュール内だけでなく、別のモジュールからでもアクセスでき、別モジュールで継承またはオーバーライドができる(Swift 2までのpublic
と同じ振る舞い) -
public
: 同じモジュール内だけでなく、別のモジュールからでもアクセスできるが、別モジュールでは継承、オーバーライドはできない -
internal
: 同じモジュール内からのみアクセスできる -
fileprivate
: 同じソースファイル内からのみアクセスできる(Swift 2までのprivate
と同じ振る舞い) -
private
: 定義されたスコープ内でのみアクセスできる
キーワードとしては open
と fileprivate
が増えたわけですが、これらはそれぞれSwift 2までの public
と private
に相当する点に注意です。むしろ、Swift 3では public
と private
の意味が変わったと考えた方がいいでしょう。
新しい public
がSwift 2までの public
と異なるのは、別モジュールでの継承・オーバーライドを認めない点です。クラスに public
を付けると、別のモジュールからでもそのクラスを使うことはできますが、そのクラスを継承することはできません。同じように、 public
を付けたメソッドは、別のモジュールからでも呼び出すことができますが、継承したクラスでそのメソッドをオーバーライドすることができません。
新しい private
はSwift 2までの private
と異なり、それが定義されたスコープ内にしかアクセスを許可しなくなりました。こちらはJavaやC#の private
に近づいた感がありますね。
どう使い分けるのか
さて、Swift 3からの5つのアクセスレベルをどのように使い分けたらいいでしょうか。
そのヒントは今回の変更点のプロポーザルにあるように思います。
-
SE-0025 Scoped Access Level ……
private
とfileprivate
を分ける理由 -
SE-0117 Allow distinguishing between public access and public overridability ……
public
とopen
を分ける理由
SE-0025の方には、従来の private
では、完全に隠したいのか、それとも関連するコード間でのみ共有したいのかが明確でなかったということが書かれています。
SE-0117の方では、サブクラス化やオーバーライドを許すことでライブラリ設計上の問題を起こしやすいことや、オーバーライドを許さなければコンパイラのコード生成における最適化がもっと期待できるというようなことが書かれています。
これらを踏まえると、私は次のように使い分けるのがいいと思いました。
- アクセスレベルを特に意識しないプログラム → 何もつけない or
internal
- アクセスレベルを意識する場合
- 別のモジュールからも使えるようにしたい →
public
- そのうち、継承させたりオーバーライドさせることが前提のもの →
open
- そのうち、継承させたりオーバーライドさせることが前提のもの →
- 実装を他からは隠したい →
private
- でも特定のコード間では共有したいとき →
fileprivate
にして共有したいコードを同じソースファイルにまとめる(うまくextensionを使うのが良さそうですね)
- でも特定のコード間では共有したいとき →
- それ以外 → 何もつけない or
internal
- 別のモジュールからも使えるようにしたい →
つまり、普段は public
, internal
, private
の3つを使うようにして、特定の意図があるときにのみ open
や fileprivate
を使うというのがいいんじゃないでしょうか。
新しいキーワードを増やすだけでなく、既存のキーワードの意味を変えてきたことからも、普段は public
や private
を使えということだろうと思います。
既存コードをXcode 8のコードマイグレーションでSwift 3にマイグレートすると、従来の public
は open
に、 private
は fileprivate
に書き換えられてしまいます。しかし、ここは注意して public
や private
に戻していきたいところです。