Skip to content

拡張機能

既存の型に機能を追加します。

拡張機能は、既存のクラス、構造体、列挙型、またはプロトコル型に新しい機能を追加します。これには、元のソースコードにアクセスできない型を拡張する機能(レトロアクティブモデリングと呼ばれる)が含まれます。拡張機能はObjective-Cのカテゴリに似ています。(Objective-Cのカテゴリとは異なり、Swiftの拡張機能には名前がありません。)

Swiftの拡張機能では次のことができます:

  • 計算型インスタンスプロパティおよび計算型タイププロパティを追加する
  • インスタンスメソッドおよびタイプメソッドを定義する
  • 新しいイニシャライザを提供する
  • サブスクリプトを定義する
  • 新しいネストされた型を定義および使用する
  • 既存の型をプロトコルに準拠させる

Swiftでは、プロトコルを拡張してその要件の実装を提供したり、準拠する型が利用できる追加機能を追加したりすることもできます。詳細については、プロトコル拡張を参照してください。

: 拡張機能は型に新しい機能を追加できますが、既存の機能をオーバーライドすることはできません。

拡張機能の構文

extensionキーワードを使用して拡張機能を宣言します:

swift
extension SomeType {
    // SomeTypeに追加する新しい機能はここに記述します
}

拡張機能を使用して、既存の型を拡張し、1つ以上のプロトコルを採用させることができます。プロトコル準拠を追加するには、クラスや構造体の場合と同じようにプロトコル名を記述します:

swift
extension SomeType: SomeProtocol, AnotherProtocol {
    // プロトコル要件の実装はここに記述します
}

この方法でプロトコル準拠を追加する方法については、拡張機能を使用したプロトコル準拠の追加で説明しています。

拡張機能を使用して、既存のジェネリック型を拡張することができます。詳細はジェネリック型の拡張で説明しています。また、ジェネリックwhere句を使用した拡張で説明しているように、条件付きで機能を追加するためにジェネリック型を拡張することもできます。

: 既存の型に新しい機能を追加するために拡張機能を定義した場合、その新しい機能は、拡張機能が定義される前に作成された既存のインスタンスでも利用可能になります。

計算型プロパティ

拡張機能は、既存の型に計算型インスタンスプロパティおよび計算型タイププロパティを追加できます。この例では、Swiftの組み込みDouble型に5つの計算型インスタンスプロパティを追加して、距離単位の基本的なサポートを提供します:

swift
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 }
}
swift
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.0Double値は「1メートル」を表すと考えられます。これがm計算型プロパティがselfを返す理由です — 1.mの式は1.0Double値を計算すると考えられます。

他の単位は、メートルで測定された値として表現するためにいくつかの変換が必要です。1キロメートルは1,000メートルと同じなので、km計算型プロパティは値を1_000.00倍してメートルで表現された数値に変換します。同様に、1メートルには3.28084フィートがあるため、ft計算型プロパティは基になるDouble値を3.28084で割ってフィートからメートルに変換します。

これらのプロパティは読み取り専用の計算型プロパティであり、簡潔さのためにgetキーワードなしで表現されています。戻り値の型はDoubleであり、Doubleが受け入れられる数学的計算内で使用できます:

swift
let aMarathon = 42.km + 195.m
print("A marathon is \(aMarathon) meters long")
// "A marathon is 42195.0 meters long"と表示されます

: 拡張機能は新しい計算型プロパティを追加できますが、格納プロパティを追加したり、既存のプロパティにプロパティオブザーバを追加したりすることはできません。

イニシャライザ

拡張機能を使用して既存の型に新しいイニシャライザを追加できます。これにより、他の型を拡張して独自のカスタム型をイニシャライザのパラメータとして受け入れたり、元の型の実装に含まれていなかった追加の初期化オプションを提供したりできます。

拡張機能を使用してクラスに新しいコンビニエンスイニシャライザを追加できますが、新しい指定イニシャライザやデイニシャライザをクラスに追加することはできません。指定イニシャライザとデイニシャライザは常に元のクラス実装によって提供される必要があります。

すべての格納プロパティにデフォルト値を提供し、カスタムイニシャライザを定義していない値型にイニシャライザを追加するために拡張機能を使用する場合、拡張機能のイニシャライザ内からその値型のデフォルトイニシャライザおよびメンバーワイズイニシャライザを呼び出すことができます。これは、値型のイニシャライザ委譲で説明されているように、イニシャライザを値型の元の実装の一部として記述した場合には当てはまりません。

別のモジュールで宣言された構造体にイニシャライザを追加するために拡張機能を使用する場合、新しいイニシャライザは定義モジュールからのイニシャライザを呼び出すまでselfにアクセスできません。

以下の例では、幾何学的な長方形を表すカスタムRect構造体を定義しています。この例では、SizePointという2つのサポート構造体も定義しており、これらのすべてのプロパティにデフォルト値0.0を提供しています:

swift
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インスタンスを作成できます:

swift
let defaultRect = Rect()
let memberwiseRect = Rect(origin: Point(x: 2.0, y: 2.0),
    size: Size(width: 5.0, height: 5.0))

Rect構造体を拡張して、特定の中心点とサイズを受け取る追加のイニシャライザを提供できます:

swift
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:)を呼び出し、新しい原点とサイズ値を適切なプロパティに格納します:

swift
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という新しいインスタンスメソッドを追加しています:

swift
extension Int {
    func repetitions(task: () -> Void) {
        for _ in 0..<self {
            task()
        }
    }
}

repetitions(task:)メソッドは、パラメータを持たず、値を返さない関数を示す型()->Voidの単一の引数を取ります。

この拡張機能を定義した後、任意の整数に対してrepetitions(task:)メソッドを呼び出して、その回数だけタスクを実行できます:

swift
3.repetitions {
    print("Hello!")
}
// Hello!
// Hello!
// Hello!

変更可能なインスタンスメソッド

拡張機能で追加されたインスタンスメソッドは、インスタンス自体を変更(またはミューテート)することもできます。selfまたはそのプロパティを変更する構造体および列挙型のメソッドは、元の実装の変更メソッドと同様に、インスタンスメソッドをmutatingとしてマークする必要があります。

次の例では、元の値を二乗する新しい変更メソッドsquareをSwiftのInt型に追加しています:

swift
extension Int {
    mutating func square() {
        self = self * self
    }
}
swift
var someInt = 3
someInt.square()
// someIntは現在9です

添字

拡張機能を使用すると、既存の型に新しい添字を追加できます。この例では、Swiftの組み込みInt型に整数の添字を追加しています。この添字[n]は、数値の右からn番目の10進数の桁を返します:

swift
extension Int {
    subscript(digitIndex: Int) -> Int {
        var decimalBase = 1
        for _ in 0..<digitIndex {
            decimalBase *= 10
        }
        return (self / decimalBase) % 10
    }
}
swift
746381295[0]
// 5を返します
746381295[1]
// 9を返します
746381295[2]
// 2を返します
746381295[8]
// 7を返します

Int値に要求されたインデックスの桁が足りない場合、添字の実装は左にゼロが埋め込まれたかのように0を返します:

swift
746381295[9]
// 0を返します。要求された場合のように:
0746381295[9]

ネストされた型

拡張機能を使用すると、既存のクラス、構造体、および列挙型に新しいネストされた型を追加できます:

swift
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値で使用できます:

swift
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のように記述できます。