Swiftの列挙型(Enumerations)

列挙型(Enumerations)

列挙型(Enumerations)です。C言語をご存知の方にはenumと書くと馴染みがあるかもしれません。Swiftの列挙型は非常に多機能で、機能としては構造体が一番近いです。ただし、値の定義方法や使い方が少し特殊ですから、慣れないと最初はとまどうかもしれません。

ここでは、列挙型の定義から始めて、列挙型を使った変数などの初期化(代入)、switch文を使った列挙型の値のチェック、列挙型のassociated valuesとraw valuesに関して、さらに再帰的列挙型について説明していきます。

列挙型(Enumerations)って何?

列挙型 = 同じ型を持つ値の集合体

タイトルに書いた通り、列挙型(Enumerations)は大雑把に言うと、

同じ型を持つ値の集合体

です。C言語に馴染みがある方はご存知だと思いますが、C言語における列挙型では、列挙された変数(名前)には必ず整数が割り当てられます。逆に言うと、整数しか割り当てることが出来ません。

Swiftの列挙型は値(正確にはraw value)に整数、浮動小数点数、文字(列)などのあらゆる型を取れます。しかしながら、そもそもSwiftの列挙型はそれぞれのケースに対して値を割り当てる必要もありません。また詳細は後述しますが、associated valuesという機能によって、列挙型が持つ値に付随する値を付与することも可能で、単純な列挙型に比べると格段に自由度が上がっています。

列挙型は特化型構造体?

このページでは実装しませんが、実は関数を内包することも可能です。また、後で少し触れますが、列挙型は値型です。そういう意味では、ちょっと特殊な構造体、と言ってもよいかもしれません。もう少し言うと、

列挙型は、同じ値型である構造体の機能を、より関連する値のグループ化に特化させたような型

であると言えるかもしれません。

列挙型構文(Enumeration Syntax)

列挙型の定義構文

列挙型を定義する時はenumキーワードを使います。

// 列挙型の定義構文
enum EnumerationName {
    // Enumeration definition
}

EnumerationNameは大文字から始めなければいけません。また、列挙型の定義は波括弧{}の中に書きます。これはクラス(classes)や構造体(structures)と同様です。

列挙型の定義 | 列挙型の値にはcaseを使う

実際に列挙型を作ってみましょう。公式マニュアルでは東西南北を表すコンパスを作っていますが、以下の例では上下左右を表す列挙型を作ってみます。

// 列挙型の例(4方向を示す列挙型)
enum Position {
    case top
    case bottom
    case left
    case right
}

列挙型内で値を定義する際には、caseキーワードを使います。先ほども書きましたが、Swiftの列挙型は、C言語のようにデフォルトで整数値を割り当てる仕様ではありません。

例えば、上記のtopは列挙型Positionという型を持った値になっており、何か特定の基本型(Intなど)が割り当てられている訳ではありません。

Swift 2.xまでは、caseの値に付ける名前は大文字から始める慣習でした。Swift 3から全て小文字に統一することになったようです。これまで「型の名前は大文字から、それ以外は小文字から」という慣習だったので、新しい仕様の方が整合性が取れているような気がします。

複数のcaseを1行にまとめて定義することも可能です。先ほどの例は、以下のように定義することも可能です。

// 複数のcaseを1行にまとめて定義
enum Position {
    case top, bottom, left, right
}

クラスと構造体のページでも言及しましたが、列挙型を定義するということはSwiftの新しい型を自作していることになります。

列挙型を使った変数(定数)の初期化や代入

さきほど作ったPosition列挙型を使って変数を初期化してみます。

// 列挙型Positionで初期化
var marginTwoPixel = Position.top
// marginTwoPixelは「Position」列挙型
marginTwoPixel = .bottom

列挙型の値を取り出す時はEnumerationName.valueという構文を使います。今、変数marginTwoPixelPosition型のtopという値で初期化しているので、この変数の型はPosition型で型推論されています

この型推論によって、marginTwoPixelには

// marginTwoPixelは「Position」列挙型
marginTwoPixel = .bottom

.valueのように、列挙型の名前を省略して代入することができます。

列挙型は値型

クラスと構造体のページで詳しく説明しましたが、

です。列挙型は、構造体と同様で、値型に分類されます。つまり、列挙型は値渡しが行われた時にインスタンス(とそのプロパティ)がコピーされます。ここでは実例を示しませんが、是非Xcodeのplayground上などでテストしてみて下さい。

列挙型の値とswitch文

Switch文の条件分岐に列挙型の値を使うことができます。先ほどのPosition列挙型を使ってみます。

// switch文で条件分岐、列挙型を使用
marginTwoPixel = .bottom
switch marginTwoPixel {
    case .top:
        print("上にマージン2pxを挿入します。")
    case .bottom:
        print("下にマージン2pxを挿入します。")
    case .left:
        print("左にマージン2pxを挿入します。")
    case .right:
        print("右にマージン2pxを挿入します。")
}
//"下にマージン2pxを挿入します。"と表示

この場合も、先ほどと同様に省エネ記法(.leftなど)が使えます。もちろんPosition.leftのように記述しても問題ありません。

先ほど「列挙型の値には特定の基本型が割り当てられていない」と書きました。上記のサンプルコードでは、どこにも基本型の情報がありません。変数に代入しているのも列挙型ですし、switch文の条件分岐にも列挙型を指定しています。繰り返しになりますが、列挙型の値自体が他の基本型の値のような働きをしていることが、この例から良く分かるのではないでしょうか。

また、上記のswitch文にはdefault制御がありませんが、Position型の全ての値を網羅しているのでコンパイルエラーにはなりません。

まとめ

  • 列挙型は値型(構造体と同じ)
  • 列挙型はenumキーワードで定義
  • 列挙型の値はcaseで定義
  • 列挙型の値をチェックしたい時はswitchを使おう