目次
列挙型とは
定義方法
ローバリュー
連想値
CaseIterableプロトコル
列挙型とは
列挙型は値型の一種で、複数の識別子をまとめる型です。例えば季節には春夏秋冬の4種類がありますが、列挙型ではこれら4つの識別子をまとめて1つの方として扱えます。列挙型の一つ一つの識別子はケースと言います。また、春であると同時に夏であることがありえないように、ケースどうしは排他的です。
定義方法
列挙型の定義にはenumキーワードとcaseキーワードを使用します。
またインスタンス化は、列挙型名.ケース名のようにケース名を指定して行います。
enum Season {
case spring
case summer
case autumn
case winter
}
let spring = Season.spring // spring
let summer = Season.summer // summer
クラスや構造体のようにイニシャライザを定義してインスタンス化を行うこともできます。
次の例では、イニシャライザinit(japaneseName:)を追加し、引数に渡された文字列に応じて各ケースをselfに代入しています。
enum Season {
case spring
case summer
case autumn
case winter
init?(japaneseName: String) {
switch japaneseName {
case "春": self = .spring
case "夏": self = .summer
case "秋": self = .autumn
case "冬": self = .winter
default: return nil // 春夏秋冬以外の場合はnilを返す
}
}
}
let april = Season(japaneseName: "春")! // spring
let august = Season(japaneseName: "夏")! // summer
ローバリュー
列挙型のケースにはそれぞれに対応する値を設定できます。この値をローバリュー(raw value)と言い、すべてのケースのローバリューの型は同じである必要があります。また、ローバリューの型に指定できるのは、数値型、String型、Character型などのリテラルに変更可能な型になります。
ローバリューを定義するには、型名の後に:とローバリューの型を記述します。
次の例では、列挙型SymbolにCharacter型のローバリュー"#"、"$"、"%"を設定します。
enum Symbol: Character {
case sharp = "#"
case dollar = "$"
case percent = "%"
}
ローバリューが定義されている列挙型では、ローバリューと列挙型の相互変換を行うための失敗可能イニシャライライザinit?(reawValue:)とプロパティrawValueが暗黙的に用意されます。init?(rawValue:)は設定したローバリューと同じ型の値を引数に取り、ローバリューが一致するケースがあればそのケースをインスタンス化し、なければnilを返します。また、rawValueプロパティは、ケースのローバリューを返します。
enum Symbol: Character {
case sharp = "#"
case dollar = "$"
case percent = "%"
}
let symbol1 = Symbol(rawValue: "#") // sharp
let character1 = symbol?.rawValue // "#"
let symbol2 = Symbol(rawValue: "&") // nil
let character2 = symbol2?.rawValue // nil
ローバリューのデフォルト値
Int型やString型ではローバリューにデフォルト値が存在し、特に値を指定しない場合はデフォルトが使用されます。
Int型のローバリューのデフォルト値は、最初のケースが0で、それ以降は前のケースに1を足した値となります。また、String型のローバリューの値は、ケース名をそのまま文字列にした値となります。
enum Symbol : Int {
case sharp
case dollar
case percent
}
Symbol.sharp.rawValue // 0
Symbol.dollar.rawValue // 1
Symbol.percent.rawValue // 2
enum Symbol : String {
case sharp
case dollar
case percent
}
Symbol.sharp.rawValue // "sharp"
Symbol.dollar.rawValue // "dollar"
Symbol.percent.rawValue // "percent"
連想値
列挙型のインスタンスは、どのケースかということに加えて、連想値(associated value)という付加情報を持つこともできます。連想値に指定できる型には制限がありません。
例えば、色の代表的な数値表現にRGB(Red,Green,Blue)とCMYK(Cyan,Magenta,Yellow,Key)がありますが、表現方法をケース、数値を連想地として表現すれば、これらを列挙型として表現できます。
enum Color {
case rgb(Float, Float, Float)
case cmyk(Float, Float, Float, Float)
}
let rgb = Color.rgb(0.0, 0.33, 0.66)
let cmyk = Color.cmyk(0.0, 0.33, 0.66, 0.99)
let color = rgb
switch color {
case .rgb(let r, let g, let b):
print("rgb: \(r),\(g),\(b)")
case .cmyk(let c, let m, let y, let k):
print("cmyk: \(c),\(m),\(y), \(k)")
}
// 実行結果 rgb: 0.0,0.33,0.66
CaseIterableプロトコル
allCasesプロパティ
列挙型を使用していると、すべてのケースを配列として取得したい場合があります。
そこでCaseIterableプロトコルへの準拠を宣言した列挙型には自動的にallCasesスタティックプロパティが追加され、このプロパティが列挙型のすべての要素を返します。
enum Season: CaseIterable {
case spring
case summer
case autumn
case winter
}
Season.allCases // [spring, summer, autumn, winter]
allCasesプロパティのコードが自動生成されない条件
通常、プロトコルに準拠するためには、プロトコルが定義しているプロパティやメソッドを実装する必要があります。しかし、上記のSeason型の例では、定義しいないにも関わらずallCasesプロパティが使用できていました。これは、連想値を持たない列挙型がCaseiterableプロトコルへの準拠を宣言した場合、その実装コードがコンパイラによって自動生成されるためです。
したがって、列挙型が連想値を持つ場合は、以下のようにallCasesプロパティを実装する必要があります。
enum Fruit: CaseIterable {
case peach
case apple(color: AppleColor)
case orange
static var allCases: [Fruit] {
return [
.peach,
.apple(color: .red),
.apple(color: .green),
.orange
]
}
}
enum AppleColor {
case red, green
}
Fruit.allCases // [peach, {red}, {green}, orange]
参考
・Swift実践入門 (https://gihyo.jp/book/2020/978-4-297-11213-4)