今でしょ!Swift - 列挙型 - 2
関連値
前のセクションの例は、列挙ケースがそれ自体を定義された(および型指定された)値にする方法を示しています。定数または変数を Planet.earth に設定し、後でこの値を確認できます。
これらのケース値と一緒に他の型の値を保持できると便利な場合があり、この追加情報を関連値と呼び、使用する度に変化させることができます。
Swift の列挙型を定義して、任意の型を関連値にできます。また、必要に応じて、列挙ケースごとに値の型を変えることができます。
例えば、在庫追跡システムが 2 つの異なる型のバーコードで製品を追跡する必要があるとします。
在庫追跡システムでは、UPC バーコードを 4 つの整数のタプルとして保存し、QR コードバーコードを任意の長さの文字列として保存するとします。
enum Barcode {
case upc(Int, Int, Int, Int)
case qrCode(String)
}
例えば次のように使用します。
var productBarcode = Barcode.upc(8, 85909, 51226, 3)
同じ変数にもう一つの型を代入することもできます。
productBarcode = .qrCode("ABCDEFGHIJKLMNOP")
この時点で、元の Barcode.upc とその整数値は、新しい Barcode.qrCode とその文字列値に置き換えられます。
Barcode の定数と変数は、.upc または .qrCode のいずれかを(関連値とともに)格納できますが、一度に格納できるのはそのうちの 1 つだけです。
switch 文を使用して様々なバーコード型を確認できます。ただし、今回は、関連値が switch 文の一部として抽出されます。switch のケースの本文内で使用するために、関連値を定数(let プレフィックス)または変数(var プレフィックス)として抽出します。
switch productBarcode {
case .upc(let numberSystem, let manufacturer, let product, let check):
print("UPC: \(numberSystem), \(manufacturer), \(product), \(check).")
case .qrCode(let productCode):
print("QR code: \(productCode).")
}
// QR code: ABCDEFGHIJKLMNOP.
列挙ケースの全ての関連値が定数として抽出される場合、または全てが変数として抽出される場合は、簡潔にするために、ケース名の前に 1 つの let または var を付けるだけで問題ありません:
switch productBarcode {
case let .upc(numberSystem, manufacturer, product, check):
print("UPC : \(numberSystem), \(manufacturer), \(product), \(check).")
case let .qrCode(productCode):
print("QR code: \(productCode).")
}
// QR code: ABCDEFGHIJKLMNOP.
Raw Values
バーコードの例は、列挙ケースが、様々な型に関連値を格納して宣言する方法を示しています。関連値の代わりに、列挙型には、全て同じ型のデフォルト値(Raw Valuesと呼ばれる)を事前に定義することもできます。
名前付きの列挙ケースと一緒に ASCII 値を格納する例
enum ASCIIControlCharacter: Character {
case tab = "\t"
case lineFeed = "\n"
case carriageReturn = "\r"
}
ここで、ASCIIControlCharacter と呼ばれる列挙型の Raw Value は、Character 型で定義されており、一般的な ASCII 制御文字のいくつかが設定されています。
Raw Value は、文字列、文字、または整数型または浮動小数点数型のいずれかです。各 Raw Value は、その列挙型内で一意でなければなりません。
暗黙的に割り当てられたRaw Value
整数または文字列の Raw Value を格納する列挙型を操作する場合、それぞれのケースに Raw Value を明示的に割り当てる必要はありません。代わりに Swift が自動的に値を割り当てます。
例えば、Raw Value に整数が使用されている場合、各ケースの暗黙の値は前のケースより 1 つ増えた値になります。最初のケースに値が設定されていない場合、その値は 0 です。
以下の列挙型は、以前の Planet の列挙型を改良したもので、太陽からの各惑星の順序を表す整数の Raw Value が含まれています
enum Planet: Int {
case mercury = 1, venus, earth, mars, jupiter, saturn, uranus, neptune
}
上記の例では、Planet.mercury の明示的な Raw Value は 1 で、Planet.venus の暗黙的な Raw Value は 2 です。
文字列が Raw Value に使用される場合、各ケースの暗黙的な値は、そのケースの名前のテキストです。
下記の列挙型は、以前の CompassPoint 列挙型を改良したもので、各方向の名前を表す文字列の Raw Value が含まれています。
enum CompassPoint: String {
case north, south, east, west
}
上記の例では、CompassPoint.south には "south" という暗黙の Raw Value があります。
rawValue プロパティを使用して列挙ケースの Raw Value にアクセスできます。
let earthsOrder = Planet.earth.rawValue
// earthsOrder は 3
let sunsetDirection = CompassPoint.west.rawValue
// sunsetDirection は "west"
Raw Valueからの初期化
Raw Value 型で列挙型を定義すると、列挙型は、Raw Value の型の値を(rawValue と呼ばれるパラメータとして)受け取り、列挙型または nil のいずれかを返すイニシャライザを自動的に提供します。
このイニシャライザを使用して、列挙型の新しいインスタンスを作成することができます。
この例では、Raw Value7 から天王星を識別します。
let possiblePlanet = Planet(rawValue: 7)
// possiblePlanet の型は Planet? で Planet.uranus と等しい
ただし、全ての Int 値が惑星に一致するわけではありません。このため、Raw Value のイニシャライザは常にオプショナルの列挙型を返します。上記の例では、possiblePlanet は Planet?または オプショナルのPlanet`型です。
位置が 11 の惑星を見つけようとすると、Raw Value のイニシャライザによって返されるオプショナルの Planet は nil になります。
let positionToFind = 11
if let somePlanet = Planet(rawValue: positionToFind) {
switch somePlanet {
case .earth:
print("ほとんど無害")
default:
print("人間にとっては安全な場所ではありません")
}
} else {
print("位置が \(positionToFind) の惑星はありません")
}
// 位置が 11 の惑星はありません
この例では、オプションバインディングを使用して、Raw Value が 11 の惑星にアクセスしようとします。
if let somePlanet = Planet(rawValue: 11)
は、オプショナルの Planet を作成し、取得できる場合は、somePlanet をそのオプショナルの Planet の値に設定します。この場合、位置が 11 の惑星を取得することはできないため、代わりに else の分岐が実行されます。
再帰的列挙型
再帰的列挙型は、1 つ以上の列挙ケースの関連値としてその列挙型の別のインスタンスを持つ列挙型です。列挙型が再帰的だということを示すには、その前に indirect を記述します。これにより、コンパイラに indirect のネストがあることを伝えることができます。