列挙体(Enumlations)
列挙体は関連する値をグループ化して定義したものです。以降では、列挙体の定義、利用法、列挙体のもつ機能についてみていきます。
列挙体の定義
列挙体はenumキーワードの後にenum名を続け、{}の中に定義を記述します。
enum 列挙体名 { //列挙体の定義 case メンバー値1 case メンバー値2 ... }
次の例は方位を示すCompassPointという名前の列挙体を定義した例です。
列挙体の中で定義された値は列挙体のメンバーとなります。caseキーワードは新しく定義されたメンバーであることを示しています。次のように複数のメンバーはセミコロンで区切って一行で書くこともできます。
enum 列挙体名 { case メンバー値1,メンバー値2,.. }
次の例はCompassPointの列挙体を一行で書き直した例です。
列挙体を定義したら、それは新しい型として定義されます。列挙体も他の型と同様に大文字で始めるのがルールです。
列挙体の利用
列挙体の値を定数・変数に代入するには次のようにします。
... = 列挙体名.メンバー値
次の例ではCompassPointの列挙体のメンバーEastを変数cpに代入しています。
if文を利用して、cpの値がEastであれば、「East」、異なれば「Not East」を出力しています。現在、cpの値はEastですので「East」がコンソールに出力されます。
代入する変数の型が列挙体であることが明らかな場合、次のような省略記法を利用することができます。
... = .メンバー値
次の例ではCompassPointの列挙体のメンバーWestを変数cpに代入しています。
今回はcpの値がWestですので「Not East」がコンソールに出力されます。
実体値(Raw Value)
実体値(Raw Value)をもつ列挙体を定義
swiftで列挙体を定義した場合、各メンバーに値は割り振られていません。しかし、各メンバーに値を割り振ることもできます。この各メンバーに割り振る値のことを実体値(Raw Value)と呼びます。
実体値を持つ列挙体は次のような構文で定義できます。
enum 列挙体名 : 型名 { case 値1 = 実体値1 case 値2 = 実体値2 ... } または enum 列挙体名 : 型名 { case 値1 = 実体値1,値2 = 実体値2,... } 型名の部分には各メンバーに割り振りたい値の型を指定します。指定できる型はInt,Double,String,Characterになります。
次の例は、Int型の固有値を持つ列挙体Weeksです。
この例ではSundayには実体値として1,Mondayには実体値として2という値を割り当てています。
また、Weeksは次のように定義することも可能です。
これはオートインクリメントと呼ばれる機能を利用してます。オートインクリメントは次のケースの実体値に「前のケースの値に+1した値」を割り当ててくれる機能です。この場合であれば、Mondayに実体値として2、Tuesdayに実体値として3、といった形で割り当ててくれます。ただし、オートインクリメントは実体値の型がInt型でのみ有効であることに注意してください。
実体値(Raw Value)へのアクセス
列挙体メンバーの実体値を取得したい場合、rawValueプロパティを利用します。次の例ではWeeks列挙体のメンバーMondayの固有値を取得しています。
Mondayには実体値として2が割り当てられているので、rawValueプロパティでアクセスした結果、「2」という値がコンソールに出力されます。
実体値をもつ列挙体の初期化処理
実体値を持つ列挙体を定義した場合、自動的にパラメータrawValueを持つイニシャライザが定義されます。イニシャライザを利用した列挙体の初期化構文は次のようになります。
xxx = 列挙体名(rawValue:値)
このイニシャライザはenumのメンバー値かnilを返します。なぜnilを返すかというとrawValueパラメータで指定された値が範囲外の値だった場合は値を割り振ることができないからです。そして、このイニシャライザはnilを返却する可能性があることから、返される値の型はOptional型となります。
次の例はWeeks列挙体のイニシャライザを利用した例です。
列挙体のイニシャライザはユーザーの入力値に応じて処理を切り分けたい時に役立つでしょう。次の例ではオプショナルバインディングを利用して、定数が列挙体の値を持っていれば値を出力して、値を持っていなければ、エラーメッセージを表示する例です。
この例では、ユーザーから入力された値が9ですので、weeksにはnilが代入されることになります。オプショナルバインディングを利用したif文では値がnilの場合、else句が実行されますので「1から7までの値を実行してください」というメッセージがコンソールに出力されます。
列挙体とメソッド
列挙体はメンバー値のほかにメソッドをもつことが可能です。メソッドの定義は関数の時と同様です。
enum 列挙体名 { //列挙体の定義 case メンバー値1 case メンバー値2 ... func メソッド名(パラメータ,..){ 文 } }
次の例ではWeeks列挙体にprintDayOfWeekメソッドを新しく追加した例です。
printDayOfWeekはインスタンス自身が現在どの値を保持しているかによって出力する曜日を変更するメソッドです。例えば、次のように固有値7で初期化されたWeeks列挙型の定数weekでprintDayOfWeekを呼び出した場合、コンソールには「土曜日」と出力されます。
列挙体とSwitch文
さて、printDayOfWeekメソッド内でif文を利用して処理の切り分けを行っていましたが、どの値と等しいかによって処理を切り分ける場合、switch文を利用するとより簡潔に分岐処理を記述することができます。注意点として、switch文は漏れなくcase句を記述する必要があります。このため、列挙体を利用したswitch文を記述する場合、メンバーをすべてcase句として列挙するか、default句を利用して値の抜けがないようにします。全てのケースをswitch文で列挙する必要がないのであればdefault句を利用すればよいでしょう。次の例では、printDayOfWeekメソッドの定義をswitch文を利用して書き直しています。
if文と比較すると、より簡潔な記述が実現できているのが確認できます。また、printDayOfWeekメソッドにおいてselfの型はWeeks列挙体であることは明らかなので、「.実体値」という省略形で記述することが可能です。
連想値(Associated Values)
これまで列挙体の各メンバーには具体的な実体値(1、2,3という具体的な数値)を割り当てていましたが、各メンバーの実体値は定義せず、どういった型の値をとるかだけを定義しておくこともできます。この各メンバーがとりうる値のことを連想値(Associated Values)と呼びます。
連想値をもつ列挙体は次のような構文で定義できます。
enum 列挙体名 { case メンバー名1(型名1,...) case メンバー名2(型名1,...) ... }
上記の構文をみて分かるとおり、連想値の型はタプルで表現します。
次の例は連想値をもつHuman列挙体です。
連想値をもつ列挙体では上記のようにメンバーごとに各メンバーに関連する型を決めておきます。
連想値(Associated Values)を持つ列挙体の場合、連想値の実際の値は利用時に決定します。次の例では変数humanにHuman列挙体の値を設定しています。なお、メンバーNameには名前を設定し、Bodyには身長と体重を設定しています。
ここでのポイントはhuman変数にHuman.NameとHuman.Bodyどちらの値も代入できることです。つまり、連想値の型が異なっても各値自体は列挙体の型ということです(Human.Name、Human.BodyどちらもHuman列挙体ということ)。この値の違いはswitch文でチェックすることができます。次の例ではhumanがどのメンバーの値をとっているかによって処理を切り分けている例です。
ここで注目してほしいのはswitch文の中で「.メンバー(パラメータ,...)」と記述すると連想値の値が取得できることです。たとえば、2番目のcase句では.Body(let height,let weight)と記述しています。このように記述すると定数heightに170、定数weightに60が代入されます。また、コメントで記述していますが、case句の前にletを置くことでパラメータ毎にletを記述しなくてもよくなります。もし、switch文の中で値の変更をしたい場合は、letではなくvarでパラメータを宣言すればよいでしょう。