# エクステンション

Swiftでは、クラスや構造体、列挙型、プロトコルなどに既存の型を拡張して機能を追加することができます。

# エクステンションの特徴

Swiftのエクステンションは次のものがあります。

# エクステンションの定義方法

エクステンションの定義方法は、extension キーワードを使って、追加したい要素を記述します。

extension 型 {
  // 追加したい要素の記述
}

# インスタンスメソッドの追加

エクステンションは、インスタンスメソッドを追加することができます。

次の例では、既存の構造体に新しいインスタンスメソッドを追加しています。

struct Person {
  var firstName: String = "Taro"
  var lastName: String = "Yamada"
}

extension Person {
  // インスタンスメソッドを追加
  func hello() {
    print("Hello \(firstName) \(lastName)")
  }
}

let person1 = Person()

person1.hello() // Hello Taro Yamada

# コンピューテッドプロパティの追加

エクステンションは、コンピューテッドプロパティを追加することができます。既存のプロパティを使用して、算出した値を返すことができます。

次の例では、既存の構造体に新しいコンピューテッドプロパティを追加しています。

struct Person {
  var firstName: String = "Taro"
  var lastName: String = "Yamada"
}

extension Person {
  // コンピューテッドプロパティを追加
  var fullName: String {
    return lastName + " " + firstName
  }
}

let person1 = Person()

person1.fullName // Yamada Taro

エクステンションは、ストアドプロパティ を追加することはできません。記述すると、コンパイラはエラーを出力します。

struct Person {
  var firstName: String = "Taro"
  var lastName: String = "Yamada"
}

extension Person {
  var fullName: String = "Yamada Taro"
}

/* 実行結果 */
// error: extensions must not contain stored properties
//   var fullName: String = "Yamada Taro"

# イニシャライザの追加

エクステンションは、イニシャライザを追加することができます。

次の例では、既存の構造体にイニシャライザを定義しています。

struct Rect {
  let x: Int
  let y: Int
}

extension Rect {
  // イニシャライザを追加する
  init(_ x: Int, _ y: Int) {
    self.x = x
    self.y = y
  }
}

let rect1 = Rect(10,10)
let rect2 = Rect(20,20)

rect1.x // 10
rect1.y // 10

rect2.x // 20
rect2.y // 20

# サブスクリプトの追加

エクステンションは、サブスクリプトを追加することができます。

標準のInt型にサブスクリプトを追加してみましょう。自身の値にインデックスを掛けた値を返しています。

extension Int {
  subscript(index: Int) -> Int {
    return self * index
  }
}

let a = 10
a[2] // 20
a[10] // 100

# where

extensionのあとに where を記述すると、拡張する条件に制約をつけることができます。whereのあとに制約を記述することによって、制約を満たす場合のみエクステンションを有効にすることができます。

# 定義方法

whereの定義方法は、extension のあとに、where キーワードと制約を記述します。

extension 型 where 制約, 制約 ... {
  // extentionの定義
}

例えば、Array<Element>型 で、Elementが String のみにエクステンションが有効になる場合を見てみましょう。

// 配列の要素が String のときだけ有効にする
extension Array where Element == String {
  func join(str: String) -> String {
    return reduce("", +)
  }
}

["a", "b", "c"].join() // abc

配列の要素が String 型のため、join メソッドを実行することができました。

TIP

reduce("", +)+ は、Swiftの標準のメソッドです。reduce関数の第2引数のクロージャとして渡しています。

@inlinable public static func + (lhs: String, rhs: String) -> String

String型以外の型を指定すると、制約を満たさないため型は拡張されず、コンパイラはエラーを出力します。

extension Array where Element == String {
  func join(str: String) -> String {
    return reduce("", +)
  }
}

[1, 2, 3].join()

/* 実行結果 */
// error: type of expression is ambiguous without more context
// [1, 2, 3].join()