※この記事はSwift by Sundellの内容を日本語に翻訳したものです。
Swiftでは、構造体として定義された型は、コンパイラーによって合成されたデフォルトの初期化子(いわゆる「メンバーごとの初期化子」)を自動的に取得します。これは、コンパイラーが指定された構造体のメンバー(つまり、その格納されたプロパティ)に基づいて生成するためです。
たとえば、 name
プロパティとpreferences
プロパティを持つ User
構造体を定義した場合、メンバーごとの初期化を使用して、これら2つのプロパティの値を渡すだけでインスタンスを作成できます。
struct User {
var name: String
var preferences: Preferences
}
let user = User(name: "John", preferences: Preferences())
一方、計算されたプロパティは、コンパイラがメンバーごとの初期化子を合成するときに完全に無視されます。したがって、初期化子を追加しても、以前と同じように上記の初期化子を使用し続けることができます。
struct User {
var name: String
var preferences: Preferences
var icon: Icon { .user }
}
let user = User(name: "John", preferences: Preferences())
Swift 5.1以降、メンバーごとの初期化子はデフォルトのプロパティ値も考慮に入れます。つまり、preferences
プロパティにデフォルト値を指定すると、name
を渡すだけでUser
インスタンスを作成できるようになります。
struct User {
var name: String
var preferences = Preferences()
}
let user = User(name: "John")
クールな点の1つは、型にprivate
プロパティがある場合でも、それらのプロパティに次のようなデフォルト値がある限り、型のメンバーごとの初期化子を使用し続けることができることです。
struct User {
var name: String
private var preferences = Preferences()
}
let user = User(name: "John")
ただし、private
プロパティにデフォルト値がない場合は、そのタイプのイニシャライザを手動で作成する必要があります。これにより、そのプロパティの値を外部から挿入できるようになります。
struct User {
var name: String
private var preferences: Preferences
init(name: String, preferences: Preferences = .init()) {
self.name = name
self.preferences = preferences
}
}
let user = User(name: "John")
// ↓でもOK
let user = User.init(name: "John")
ただし、メンバーごとのイニシャライザーのアクセスレベルがinternal
より高くなることはありません。つまり、タイプが定義されているモジュール内でのみ内部的に使用できます。
これは最初は奇妙な制限のように思えるかもしれませんが、データの内部構造に縛られることなく、常にパブリック消費用の明示的なAPIを設計する必要があるため、メリットがあります。
したがって、要約すると、次の場合に構造体のメンバーごとの初期化子を使用できます。
- そのすべてのメンバーが表示されるか、デフォルト値がある場合。
- 構造体が定義されているのと同じモジュール内にインスタンスを作成している場合。
他のすべてのケースでは、少なくとも今のところ、初期化子を手動で実装する必要があります。
元の記事
When can a struct’s memberwise initializer be used? Swift by Sundell