拡張機能
既存の型に機能を追加します。
拡張機能は、既存のクラス、構造体、列挙型、またはプロトコル型に新しい機能を追加します。これには、元のソースコードにアクセスできない型を拡張する機能(レトロアクティブモデリングと呼ばれる)が含まれます。拡張機能はObjective-Cのカテゴリに似ています。(Objective-Cのカテゴリとは異なり、Swiftの拡張機能には名前がありません。)
Swiftの拡張機能では次のことができます:
- 計算型インスタンスプロパティおよび計算型タイププロパティを追加する
- インスタンスメソッドおよびタイプメソッドを定義する
- 新しいイニシャライザを提供する
- サブスクリプトを定義する
- 新しいネストされた型を定義および使用する
- 既存の型をプロトコルに準拠させる
Swiftでは、プロトコルを拡張してその要件の実装を提供したり、準拠する型が利用できる追加機能を追加したりすることもできます。詳細については、プロトコル拡張を参照してください。
注: 拡張機能は型に新しい機能を追加できますが、既存の機能をオーバーライドすることはできません。
拡張機能の構文
extension
キーワードを使用して拡張機能を宣言します:
extension SomeType {
// SomeTypeに追加する新しい機能はここに記述します
}
拡張機能を使用して、既存の型を拡張し、1つ以上のプロトコルを採用させることができます。プロトコル準拠を追加するには、クラスや構造体の場合と同じようにプロトコル名を記述します:
extension SomeType: SomeProtocol, AnotherProtocol {
// プロトコル要件の実装はここに記述します
}
この方法でプロトコル準拠を追加する方法については、拡張機能を使用したプロトコル準拠の追加で説明しています。
拡張機能を使用して、既存のジェネリック型を拡張することができます。詳細はジェネリック型の拡張で説明しています。また、ジェネリックwhere句を使用した拡張で説明しているように、条件付きで機能を追加するためにジェネリック型を拡張することもできます。
注: 既存の型に新しい機能を追加するために拡張機能を定義した場合、その新しい機能は、拡張機能が定義される前に作成された既存のインスタンスでも利用可能になります。
計算型プロパティ
拡張機能は、既存の型に計算型インスタンスプロパティおよび計算型タイププロパティを追加できます。この例では、Swiftの組み込みDouble
型に5つの計算型インスタンスプロパティを追加して、距離単位の基本的なサポートを提供します:
extension Double {
var km: Double { return self * 1_000.0 }
var m: Double { return self }
var cm: Double { return self / 100.0 }
var mm: Double { return self / 1_000.0 }
var ft: Double { return self / 3.28084 }
}
let oneInch = 25.4.mm
print("One inch is \(oneInch) meters")
// "One inch is 0.0254 meters"と表示されます
let threeFeet = 3.ft
print("Three feet is \(threeFeet) meters")
// "Three feet is 0.914399970739201 meters"と表示されます
これらの計算型プロパティは、Double
値が特定の長さの単位として考慮されるべきであることを表しています。計算型プロパティとして実装されていますが、これらのプロパティの名前はドット構文を使用して浮動小数点リテラル値に追加でき、そのリテラル値を使用して距離変換を行う方法として使用できます。
この例では、1.0
のDouble
値は「1メートル」を表すと考えられます。これがm
計算型プロパティがself
を返す理由です — 1.m
の式は1.0
のDouble
値を計算すると考えられます。
他の単位は、メートルで測定された値として表現するためにいくつかの変換が必要です。1キロメートルは1,000メートルと同じなので、km
計算型プロパティは値を1_000.00
倍してメートルで表現された数値に変換します。同様に、1メートルには3.28084
フィートがあるため、ft
計算型プロパティは基になるDouble
値を3.28084
で割ってフィートからメートルに変換します。
これらのプロパティは読み取り専用の計算型プロパティであり、簡潔さのためにget
キーワードなしで表現されています。戻り値の型はDouble
であり、Double
が受け入れられる数学的計算内で使用できます:
let aMarathon = 42.km + 195.m
print("A marathon is \(aMarathon) meters long")
// "A marathon is 42195.0 meters long"と表示されます
注: 拡張機能は新しい計算型プロパティを追加できますが、格納プロパティを追加したり、既存のプロパティにプロパティオブザーバを追加したりすることはできません。
イニシャライザ
拡張機能を使用して既存の型に新しいイニシャライザを追加できます。これにより、他の型を拡張して独自のカスタム型をイニシャライザのパラメータとして受け入れたり、元の型の実装に含まれていなかった追加の初期化オプションを提供したりできます。
拡張機能を使用してクラスに新しいコンビニエンスイニシャライザを追加できますが、新しい指定イニシャライザやデイニシャライザをクラスに追加することはできません。指定イニシャライザとデイニシャライザは常に元のクラス実装によって提供される必要があります。
すべての格納プロパティにデフォルト値を提供し、カスタムイニシャライザを定義していない値型にイニシャライザを追加するために拡張機能を使用する場合、拡張機能のイニシャライザ内からその値型のデフォルトイニシャライザおよびメンバーワイズイニシャライザを呼び出すことができます。これは、値型のイニシャライザ委譲で説明されているように、イニシャライザを値型の元の実装の一部として記述した場合には当てはまりません。
別のモジュールで宣言された構造体にイニシャライザを追加するために拡張機能を使用する場合、新しいイニシャライザは定義モジュールからのイニシャライザを呼び出すまでself
にアクセスできません。
以下の例では、幾何学的な長方形を表すカスタムRect
構造体を定義しています。この例では、Size
とPoint
という2つのサポート構造体も定義しており、これらのすべてのプロパティにデフォルト値0.0
を提供しています:
struct Size {
var width = 0.0, height = 0.0
}
struct Point {
var x = 0.0, y = 0.0
}
struct Rect {
var origin = Point()
var size = Size()
}
Rect
構造体はすべてのプロパティにデフォルト値を提供しているため、デフォルトイニシャライザで説明されているように、デフォルトイニシャライザとメンバーワイズイニシャライザを自動的に受け取ります。これらのイニシャライザを使用して新しいRect
インスタンスを作成できます:
let defaultRect = Rect()
let memberwiseRect = Rect(origin: Point(x: 2.0, y: 2.0),
size: Size(width: 5.0, height: 5.0))
Rect
構造体を拡張して、特定の中心点とサイズを受け取る追加のイニシャライザを提供できます:
extension Rect {
init(center: Point, size: Size) {
let originX = center.x - (size.width / 2)
let originY = center.y - (size.height / 2)
self.init(origin: Point(x: originX, y: originY), size: size)
}
}
この新しいイニシャライザは、提供された中心点とサイズ値に基づいて適切な原点を計算することから始めます。次に、構造体の自動メンバーワイズイニシャライザinit(origin:size:)
を呼び出し、新しい原点とサイズ値を適切なプロパティに格納します:
let centerRect = Rect(center: Point(x: 4.0, y: 4.0),
size: Size(width: 3.0, height: 3.0))
// centerRectの原点は(2.5, 2.5)で、サイズは(3.0, 3.0)です
注意: 拡張機能で新しいイニシャライザを提供する場合、イニシャライザが完了した時点で各インスタンスが完全に初期化されていることを確認する責任があります。
メソッド
拡張機能を使用して既存の型に新しいインスタンスメソッドおよび型メソッドを追加できます。次の例では、Int
型にrepetitions
という新しいインスタンスメソッドを追加しています:
extension Int {
func repetitions(task: () -> Void) {
for _ in 0..<self {
task()
}
}
}
repetitions(task:)
メソッドは、パラメータを持たず、値を返さない関数を示す型()->Void
の単一の引数を取ります。
この拡張機能を定義した後、任意の整数に対してrepetitions(task:)
メソッドを呼び出して、その回数だけタスクを実行できます:
3.repetitions {
print("Hello!")
}
// Hello!
// Hello!
// Hello!
変更可能なインスタンスメソッド
拡張機能で追加されたインスタンスメソッドは、インスタンス自体を変更(またはミューテート)することもできます。self
またはそのプロパティを変更する構造体および列挙型のメソッドは、元の実装の変更メソッドと同様に、インスタンスメソッドをmutating
としてマークする必要があります。
次の例では、元の値を二乗する新しい変更メソッドsquare
をSwiftのInt
型に追加しています:
extension Int {
mutating func square() {
self = self * self
}
}
var someInt = 3
someInt.square()
// someIntは現在9です
添字
拡張機能を使用すると、既存の型に新しい添字を追加できます。この例では、Swiftの組み込みInt
型に整数の添字を追加しています。この添字[n]
は、数値の右からn番目の10進数の桁を返します:
extension Int {
subscript(digitIndex: Int) -> Int {
var decimalBase = 1
for _ in 0..<digitIndex {
decimalBase *= 10
}
return (self / decimalBase) % 10
}
}
746381295[0]
// 5を返します
746381295[1]
// 9を返します
746381295[2]
// 2を返します
746381295[8]
// 7を返します
Int
値に要求されたインデックスの桁が足りない場合、添字の実装は左にゼロが埋め込まれたかのように0
を返します:
746381295[9]
// 0を返します。要求された場合のように:
0746381295[9]
ネストされた型
拡張機能を使用すると、既存のクラス、構造体、および列挙型に新しいネストされた型を追加できます:
extension Int {
enum Kind {
case negative, zero, positive
}
var kind: Kind {
switch self {
case 0:
return .zero
case let x where x > 0:
return .positive
default:
return .negative
}
}
}
この例では、Int
に新しいネストされた列挙型を追加しています。この列挙型はKind
と呼ばれ、特定の整数が表す数の種類を表します。具体的には、その数が負、ゼロ、または正であるかどうかを表します。
この例では、Int
に新しい計算インスタンスプロパティkind
も追加しており、この整数に対して適切なKind
列挙ケースを返します。
ネストされた列挙型は、任意のInt
値で使用できます:
func printIntegerKinds(_ numbers: [Int]) {
for number in numbers {
switch number.kind {
case .negative:
print("- ", terminator: "")
case .zero:
print("0 ", terminator: "")
case .positive:
print("+ ", terminator: "")
}
}
print("")
}
printIntegerKinds([3, 19, -27, 0, -6, 0, 7])
// "+ + - 0 - 0 + "を出力します
この関数printIntegerKinds(_:)
は、Int
値の入力配列を受け取り、それらの値を順に反復処理します。配列内の各整数について、関数はその整数のkind
計算プロパティを考慮し、適切な説明を出力します。
注:
number.kind
はすでにInt.Kind
型であることが知られています。このため、すべてのInt.Kind
ケース値は、switch文内で省略形で記述できます。例えば、Int.Kind.negative
ではなく.negative
のように記述できます。