LoginSignup
2
6

More than 3 years have passed since last update.

【Swift】列挙型について

Posted at

1.はじめに


Swiftにはまだ紹介していない、列挙型という非常に重要な記法があります。
列挙型は利用すれば大きな効果を発揮する場面が非常に多いので、特にSwift初心者の方はぜひ私の記事を見ていってください。
なお列挙型には共用型の列挙型というものもありますが、こちらについては次回解説したいと思います。

2.列挙型とは


Swiftでの列挙型ではそれ独自のメソッドを定義できるなど、C言語での列挙型に比べるとかなり拡張されたものになっています。
以下にシンプルな例を示します。

enum Direction {
    case up
    case down
    case right
    case left
}

これで列挙型Directionを定義することができました。
upやdownなどは列挙ケース(ケース)と呼びます。ケース名は小文字開始のキャメルケースで記述するのが通例です。

また先ほどの例は次のように書くこともできます。

enum Direction {
    case up, down, right, left
}

ここで宣言した列挙ケースは変数や定数に代入できます。
ケース名はグローバルではないため、列挙型の名前と「.」を使って記述できます。
ただし使われる列挙型が明らかな場合は列挙型名を省略することができます。
つまりケース名が単独で使われることはないため、他の列挙型のケース名と重複しても問題はありません。例を示します。

let d = Direction.up          
var x : Direction = .right    // 型が決まっていれば"."から記述できる
d == x                        // これらは違う値(false)

3.メソッドの定義


C言語と違いSwiftでは列挙型の定義にメソッドを含めて、型全体としての動作や機能を表すことができます。
たとえば、先ほど挙げた例の4つの方向を示す列挙型で、それぞれの方向から時計回りで90°開店した向きを知りたいとします。これは次のようなメソッドで定義できます。

enum Direction {
    case up, down, right, left
    func clockwise() -> Direction {
        switch self {
            case .up:    return .right
            case .down:  return .left
            case .right: return .down
            case .left:  return .up
        }
    }
}

たとえばこのメソッドは次のように使うことができます。

let d = Direction.up
d.clockwise() == Direction.down              // false
d.clockwise().clockwise() == Direction.down  // true

このように、列挙型のケースとそれに依存した手続きを一体のものとして定義することができます。

4.値型の列挙型


Swiftの列挙型には、ここまでで説明してきたシンプルな列挙型に加えて、全ケースが同じデータ型の何らかの値を持つようになっている「値型」があります。
値型の列挙型では、すべての列挙ケースが、同じ型の互いに異なる値を持つように定義できます。
値の型を、その列挙型実体型呼び、各ケースに割り当てられた値を実体値と呼びます。
新しく定義する型名の後に、「:」と実体型を記述します。
それぞれのケースには値を記述することができますが、ここで記述できるのは整数、実数、Bool値、または文字列リテラルだけです。
従って実体型になることができるのは、これらの値で初期化ができる型ということになります。次の概要を確認してください。

enum 型名:実体型 {
    case ケース名 = リテラル
    case ケース名 = リテラル
    ... ...

実体型が整数の場合、リテラルで値を指定しなかったケースは、1つ前のケースの値に1を加えたものになります。
先頭のケースにリテラルがない場合、値は0となります。
値は自由に指定できますが、同じ値を持つケースがないようにしなければなりません。

実体型が文字列で、リテラルを指定しない場合、ケース名の文字列が実体型の値となります。
整数、文字列以外の型が実体型の場合、各ケースに対して必ずリテラルを記述し、それぞれが異なる値を持つようにしなければなりません。

列挙型とその実体型の間で、型を相互に変換することができます。
次の例を確認してください。以前例で取り上げたDirection型を、Int型を実体型とするように定義します。

enum Direction : Int {
    case up = 0, down, right, left
}

最初のupの実体値を0にしたので、それ以降のdown、right、leftはそれぞれ1、2、3の値を持つことになります。
このような列挙型のインスタンスをいくつか生成したとして、それぞれが持つ実体値を取り出すために、rawValueというプロパティを使うことができます。

let a = Direction.right
let i = a.rawValue                // i = 2(Int)
let k = Direction.down.rawValue   // k = 1(Int)

逆に、実体型の値からそれに対応する列挙型のインスタンスを得るには、イニシャライザinit?(rawValue:)を使います。
実体型の値に対応する列挙ケースが存在しない場合には正しい値が返せないので、このイニシャライザは失敗のあるイニシャライザです。以下の例を見てください。

let b : Direction? = Direction(rawValue:3)
b! == Direction.left                  // true
if let c = Direction(rawValue:2) {    // オプショナル束縛構文
    print("OK \(c.rawValue)")         // "OK 2"
}

値型の列挙型を使って、先ほど書いた概要を書き直してみます。
方向を表すケースを時計回りの順に定義すれば、switch文ですべてを列挙する必要はなくなります。
関数の中でインスタンス自体を表す必要がありますが、このためには予約語のselfを使います。例を確認してください。

enum Direction : Int {
    case up = 0, right, down, left       // 時計回りに並べ直す
    func clockwise() -> Direction {
        let t = (self.rawValue + 1) % 4  // selfはインスタンス自体
        return Direction(rawValue:t)!    // nilにはならない

5.列挙型に対するメソッドとプロパティ


列挙型にもプロパティは定義できますが、構造体と異なり、定義できるのは計算型のプロパティだけです。1つ例を挙げます。

enum Direction : Int {
    case up = 0, right, down, left  // 時計回り
    /* ... 中略 ... */
    var horizontal: Bool {          // get節のみのプロパティ定義
        switch self {
        case .right, .left: return true
        default: return false
        }
    }
    mutating func turnBack() {
        self = Direction(rawValue:((self.rawValue + 2) % 4))!
    }
}

ここまでで例として挙げてきたDirection型について、値が水平方向、右か左を表している場合はtrueを返す計算型のプロパティhorizontalを定義しています。
この例ではさらに、列挙型に対してmutating属性を持つメソッドを定義する例も示しています。

構造体とは異なり、自分自身を表すselfに別の値を代入することで、変数に格納されている列挙型自体の値を変更します。
値が定数に格納されている場合には利用できません。
ここではDirection型に、正反対の方向に反転するメソッドturnBackを追加しています。

実行例を見てみましょう。

var d = Direction.left
print(d.rawValue)    // 3が出力される
d.turnBack()
print(d.rawValue)    // 1が出力される(3の反対方向)
print(d.horizontal)  // trueが出力される

6.列挙型のイニシャライザとタイプメソッド


列挙型にも、構造体と同様にタイプメソッド、タイププロパティ、イニシャライザを定義することができます。
タイプメソッドとタイプメソッドは、関数定義、あるいは変数定義の先頭にキーワードstaticを記述します。
イニシャライザでは、selfに何を代入するのかを定義することになります。
自動的に用意されるイニシャライザinit?(rawValue:)を再定義することも可能です。以下の例を確認してください。

enum Direction : Int {
    case up = 0, right, down, left
    static var defaultDirection = Direction.up
    init() {
        self = Direction.defaultDirection
    }
    // ... 中略 ...
}

ここではdefaultDirectionという変数を用意し、イニシャライザinit()を使ってインスタンスを生成した際には、この値を初期値とするようにしています。

実行例を示します。

Direction.defaultDirection = .right    // 右向きを初期値にする
let e = Direction()                    // イニシャライザを使う
let g = Direction(rawValue:1)          // 値型の列挙型のイニシャライザ
print(e == g)                          // true

7.おわりに


今回はSwiftにおいて非常に重要な記法である、列挙型について解説しました。
次回は共用型の列挙型について記事を書きますので、来週記事がUPされましたらそちらもぜひご覧ください。
ここまで読んでくださった方ありがとうございました。

2
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
6