# 列挙型とは
列挙型とは、値型の一種で、関連する値のグループに共通の型を定義してまとめたものです。
# 列挙型の特徴
Swiftの列挙型は次のような特徴があります。
- 構造体やクラス同様にプロパティやメソッドなどの定義が可能
- ただし、ストアドプロパティを持つことはできない
- 値型 enum | Raw value enum で定義可能
- 関連型 enum | Associated value enum で定義可能
# 列挙型の定義方法
列挙型の定義方法は、enum
キーワードと case
キーワードを使用して定義します。
enum 型名 {
case ケース名
case ケース名
case ケース名
}
次の例では、Animal型を作り、dog、cat、rabbitのケースを定義しています。
enum Animal {
case dog
case cat
case rabbit
}
列挙型は、型.ケース
でインスタンス化させることができます。
enum Animal {
case dog
case cat
case rabbit
}
let dog = Animal.dog // dog
let cat = Animal.cat // cat
型が予めわかっている場合は、.
だけで呼び出すこともできます。
enum Animal {
case dog
case cat
case rabbit
}
// 型を定義
var a: Animal
// すでに型がわかっているので、Animalを省略して書ける
a = .dog // dog
# イニシャライザ
列挙型は、init
キーワードを使ってイニシャライザを定義することができます。
enum Animal {
case dog
case cat
case rabbit
init?(animal: String) {
switch animal {
case "dog": self = .dog
case "cat": self = .cat
case "rabbit": self = .rabbit
default: return nil
}
}
}
let dog = Animal(animal: "dog") // Optional(dog)
let hamster = Animal(animal: "hamster") // nil
イニシャライザで、引数に渡された値によって、各ケースを代入しています。ケースが存在しなければnilを返しています。
# プロパティ
列挙型のプロパティは、コンピューテッドプロパティ と タイププロパティ を使用することができます。プロパティの定義には制限があり、ストアドプロパティを使うことはできません。
enum Animal {
case dog
case cat
case rabbit
// コンピューテッドプロパティを定義する
var feed: String {
switch self {
case .dog: return "お肉"
case .cat: return "お魚"
case .rabbit: return "にんじん"
}
}
// タイププロパティを定義する
static let limit = 100
}
Animal.dog.feed // お肉
Animal.limit // 100
ストアドプロパティを定義すると、コンパイラはエラーを出力します。
enum Animal {
case dog
case cat
case rabbit
var limit = 100
}
/* 実行結果 */
// error: enums must not contain stored properties
# メソッド
Swiftの列挙型は、メソッドを定義することができます。
enum Animal {
case dog
case cat
case rabbit
// メソッドを定義
func sound() -> String {
switch self {
case .dog: return "bark"
case .cat: return "meow"
case .rabbit: return "boing-boing"
}
}
}
Animal.dog.sound() // bark
switch文の中で、全てのケースが網羅されていないと、Swiftは整合性を保つためにエラーを出力します。
enum Animal {
case dog
case cat
case rabbit
func sound() -> String {
switch self {
case .dog: return "bark"
case .cat: return "meow"
// rabbitのケースがない
// case .rabbit: return "boing-boing"
}
}
}
/* 実行結果 */
// error: switch must be exhaustive
// switch self {
// note: add missing case: '.rabbit'
mutating メソッドを使うと列挙型自身を変更することができます。
enum Code: Int {
case a
case b
case c
// mutating で変更可能にする
mutating func update(code: Code) {
self = code
}
}
var a = Code.a
a.update(code: .b)
a.rawValue // 1
# ネスト
列挙型は、ネストして記述することができます。ネストすることにより階層構造を明確にして、簡潔に書くことができます。
enum Animal {
enum Dog {
case bulldog
case poodle
case beagle
}
enum Cat {
case benegal
case britishShorthair
case persian
}
}
Animal.Dog.bulldog // bulldog
Animal.Cat.benegal // benegal
# 値型 enum | Raw value enum
列挙型のケースに設定する値をRaw valueといいます。Raw valueは全て同じ型でなければなりません。Int型、Double型、String型、Character型などを指定することができますが、その他の型を割り当てようとすると、コンパイラはエラーを出力します。
Raw valueは、ケースに値を代入して定義します。
enum Programming: Int {
case swift = 1
case ruby = 2
case php = 3
case javascript = 4
}
enum BloodType: String {
case a = "A"
case b = "B"
case o = "O"
case ab = "AB"
}
それ以外の型を指定すると、コンパイラはエラーを出力します。次の例では、構造体を代入しています。
struct Rectangle {
var width: Int
var height: Int
}
enum RectangleSize {
case sm = Rectangle(width: 100, height: 100)
case md = Rectangle(width: 200, height: 200)
case lg = Rectangle(width: 300, height: 300)
}
/* 実行結果 */
// error: raw value for enum case must be a literal
// case sm = Rectangle(width: 100, height: 100)
// error: raw value for enum case must be a literal
// case md = Rectangle(width: 200, height: 200)
// error: raw value for enum case must be a literal
// case lg = Rectangle(width: 300, height: 300)
# イニシャライザ
Raw valueが定義されると、暗黙的に rawValue
を引数にとる init?(失敗可能イニシャライザ) と プロパティの rawValue
が作られます。
そのため、インスタンス化のときに rawValue
を引数に渡すと対応するケースが返ってきます。値は、ケース.rawValue
で取得することができます。
enum BloodType: String {
case a = "A"
case b = "B"
case o = "O"
case ab = "AB"
}
// rawValue を引数に渡す
let a = BloodType(rawValue: "A")
// 値を取得する
a?.rawValue // A
// 直接アクセスしても取得できる
BloodType.a.rawValue // A
# デフォルト値
Raw valueに値を設定しなかった場合は、デフォルト値がセットされます。数値型の場合は、一番上のケースが0からインクリメントされ、String型の場合はケース名が値と同じになります。
enum Programming: Int {
case swift
case ruby
case php
case javascript
}
enum BloodType: String {
case a
case b
case o
case ab
}
Programming.swift.rawValue // 0
Programming.ruby.rawValue // 1
Programming.php.rawValue // 2
Programming.javascript.rawValue // 3
BloodType.a.rawValue // a
BloodType.b.rawValue // b
BloodType.o.rawValue // o
BloodType.ab.rawValue // ab
# 関連型 enum | Associated value enum
関連型 enum
を使用すると、ケースに付属的に関連する値をつけることができます。関連値はあらかじめ決められて値を初期化しないので、値型 enumのように型の制限もありません。
次の例では、サイトに応じてレスポンシブにするブレイクポイントを定義しています。
enum MediaQuery {
// ケースとそれに付属する値の型を定義
case breakpoint(xs: Int, sm: Int, md: Int, lg: Int, xl: Int)
}
次に、定義したケースに対して具体的な値を設定します。サイトによってブレイクポイントは異なるので、別々の値を定義しています。
let breakpoint1 = MediaQuery.breakpoint(xs: 0, sm: 600, md: 960, lg: 1280, xl: 1920)
let breakpoint2 = MediaQuery.breakpoint(xs: 0, sm: 640, md: 1020, lg: 1480, xl: 1920)
定義した値を使うために、メソッドを定義しましょう。ケースからの値を抽出して配列形式で返します。
enum MediaQuery {
case breakpoint(xs: Int, sm: Int, md: Int, lg: Int, xl: Int)
// メソッドを定義
func breakpoints() -> [Int] {
switch self {
case .breakpoint(let xs, let sm, let md, let lg, let xl):
return [xs, sm, md, lg, xl]
}
}
}
そして、このメソッドを実行してケースの値を取得することができます。
breakpoint1.breakpoints() // [0, 600, 960, 1280, 1920]
breakpoint2.breakpoints() // [0, 640, 1020, 1480, 1920]
関連値を使うことで、柔軟にデータを作ることができました。関連型enumは、インスタンスごとに異なる値を持たせたい場合に使うのがいいでしょう。
# allCases
列挙型は、CaseIterable
プロトコルに準拠させることで、ケースを配列で取得することができます。
次の例では、CaseIterable
を準拠させて配列で取得しています。
enum Animal: CaseIterable {
case dog
case cat
case rabbit
}
Animal.allCases // [dog, cat, rabbit]