Swiftではクラスや構造体、列挙型の定義の中に、さらにクラスや構造体、列挙型を定義することができます。つまり型をネストして定義できるということです。
このネストの階層には制限がありません。ネストした型のさらに内部に型を定義できます。
具体例を次に示します。
// パーソンクラス
class Person {
// 血液型
enum BloodType: String {
case A = "A"
case B = "B"
case O = "O"
case AB = "AB"
var asString: String {
return self.toRaw() + "型"
}
}
// 体格
struct BodySize {
// 体型
enum Shape {
case Normal, Fat, Thin
var asString: String {
switch self {
case Normal:
return "標準体型"
case Fat:
return "太り気味"
case Thin:
return "痩せ気味"
}
}
}
var height: Float // 身長
var weight: Float // 体重
var bmi: Float { // BMI ( = 体重(Kg) / (身長(m) x 身長(m)) )
return weight / powf(height / 100.0, 2.0)
}
var shape: Shape { // 体型
switch bmi {
case let v where v < 18.5:
return .Thin
case let v where v >= 25.0:
return .Fat
default:
return .Normal
}
}
}
let name: String // 名前
var bloodType: BloodType // 血液型
var bodySize: BodySize // 体格
// イニシャライザ
init(name: String, bloodType: BloodType, height: Float, weight: Float) {
self.name = name
self.bloodType = bloodType
self.bodySize = BodySize(height: height, weight: weight)
}
// 詳細出力
func printDescription() {
print("名前:\(name) 血液型:\(bloodType.asString) 身長:\(bodySize.height)cm 体重:\(bodySize.weight)kg 体型:\(bodySize.shape.asString)")
}
}
let suzuki = Person(name: "鈴木花子", bloodType: .A, height: 155.0, weight: 45.0)
print(suzuki.bodySize.height)
/* 実行結果
155.0
*/
let abe = Person(name: "阿部れいじ", bloodType: .A, height: 170.0, weight: 60.0)
abe.printDescription()
/* 実行結果
名前:阿部れいじ 血液型:A型 身長:170.0cm 体重:60.0kg 体型:標準体型
*/
この例では、パーソンクラスの定義の中に、血液型を表す列挙型と、体格用の構造体を定義しています。また、体格用の構造体の中にさらに体型を表す列挙型を定義しています。
体型は、身長と体重から求められるBMI(ボディマス指数:体重(Kg) ÷ 身長(m)2で算出)というものを使って、標準体型か痩せ気味か太り気味かを判定して列挙型で返しています。
このように、ある型に固有の属性として使用する型や、その型の定義のために一時的に使用するような型を内部で定義することで、その型特有の定義であることが明確になり、またコードのメンテナンス性も向上します。
ネストした型をそれが属する型の外側のスコープから使用することもできます。例えば、上のパーソンクラスの中で定義している体型の列挙型も次のようにして使うこともできます。
let shape = Person.BodySize.Shape.Fat
print(shape.asString)
/* 実行結果
太り気味
*/