Menu
Coddy logo textTech

プロトコル拡張

CoddyのSwiftジャーニー「オブジェクト指向プログラミング」セクションの一部。レッスン 29/57。

プロトコル拡張は、プロトコルのメソッドにデフォルト実装を与えます。準拠するすべての型はそれを無料で利用でき、型は独自のバージョンを提供することもできます。

protocol Greeter {
    var name: String { get }
    func greet() -> String
}

extension Greeter {
    func greet() -> String { "Hello, \(name)!" }
}

struct English: Greeter { let name: String }
struct Pirate: Greeter {
    let name: String
    func greet() -> String { "Ahoy, \(name)!" }
}

print(English(name: "Ada").greet())
print(Pirate(name: "Ada").greet())

出力:

Hello, Ada!
Ahoy, Ada!

エクステンションは、要件だけをもとに構築された新しいメソッドを追加することもできます。適合する型は、必要な小さな部分を記述するだけで、残りを受け取れます。

protocol Scored {
    var points: [Int] { get }
}

extension Scored {
    var total: Int { points.reduce(0, +) }
    var best: Int { points.max() ?? 0 }
    func summary() -> String { "total \(total), best \(best)" }
}

struct Player: Scored {
    let points: [Int]
}

print(Player(points: [3, 9, 4]).summary())

出力:

total 16, best 9

これは継承を使わずに、構造体、クラス、列挙型の間で振る舞いを共有します。標準ライブラリも同じ仕組みで動作します。すべての Collection は、プロトコル拡張から isEmpty、first、map を取得します。

extension Collection {
    // isEmpty, first, map, filter, ... はすべてここで一度定義されています
    // そしてすべての array、set、dictionary、string がそれらを取得します
}

制約付きの拡張は、where を使って、一部の適合する型にのみメソッドを追加します。

protocol Container {
    associatedtype Item
    var items: [Item] { get }
}

extension Container where Item == Int {
    var sum: Int { items.reduce(0, +) }
}

struct Box: Container {
    var items: [Int]
}

print(Box(items: [2, 5]).sum)

出力:

7
challenge icon

チャレンジ

簡単

Priced プロトコルを宣言し、var price: Int { get }(セント単位)と var formatted: String { get } を要求します。プロトコル拡張で、formatted に $4.50 を返すデフォルト実装を与え、価格が予算内に収まるかどうかを返す isAffordable(budget:) メソッドを追加します。構造体 Coffee(価格は 450)と Sandwich(格納プロパティ price)を準拠させます。Coffee では、デフォルトの formatted を coffee $4.50 に置き換える必要があります。

用意されたコードは予算を読み取り、その後に coffee または sandwich,650 の行を読み取ります。各項目について formatted と yes または no を出力します。

コードは Priced.swift、Coffee.swift、Sandwich.swift に記述します。main.swift には用意された入出力コードが含まれており、編集できません。

自分で試してみよう

// 提供された入出力コード: そのままにしておく
var input: [String] = []
while let line = readLine() { input.append(line) }
let budget = Int(input[0])!
for line in input.dropFirst() {
    let item: any Priced = line == "coffee" ? Coffee() : Sandwich(price: Int(line.split(separator: ",")[1])!)
    print("\(item.formatted) \(item.isAffordable(budget: budget) ? "yes" : "no")")
}
quiz icon腕試し

このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。

オブジェクト指向プログラミングのすべてのレッスン

自分で練習してみよう: Swiftプレイグラウンド