Swift の Subscript について

  • 119
    いいね
  • 0
    コメント
この記事は最終更新日から1年以上が経過しています。

Subscript って?

Dictionary とか Array で hoge["fuga"] みたいな感じで、要素にアクセスするアレ。
Swift では、アレを自分でクラスや構造体に対して、開発者自身が定義して使うことが出来ます。

基本的な書き方

書き方には幾つかのパターンがあります。
基本的な書式に関しては通常の func と似ています。

読み書き可能な場合

get/set ステートメントを用いて、読み込みと書き込みそれぞれの処理内容を記述することが可能です。
set ステートメントの引数部分は省略することも可能です、その場合セット対象の値は newValue という変数に格納されます。

定義部分

Sample1

class SubscriptSample {
    var userNames: Array<String>

    init(){
        userNames = []
    }

    // subscript の受け取る引数は [] の中で渡すもの
    subscript(index: Int) -> String {
        get {
            assert(userNames.count > index, "index out of range")
            return userNames[index] + " さん 昨夜はお楽しみでしたね"
        }
        set(name) {
            assert(0 > index || userNames.count >= index, "index out of range")
            userNames.insert(name, atIndex: index)
        }
    }
}

使い方

Sample2

func test() {
    let sample = SubscriptSample()
    sample[0] = "happy_ryo"
    sample[1] = "crazy_ryo"
    println(sample[0])
    println(sample[1])
}

実行結果


happy_ryo さん 昨夜はお楽しみでしたね // println(sample[0]) の出力
crazy_ryo さん 昨夜はお楽しみでしたね // println(sample[1]) の出力

読み込み専用の場合

読み込み専用の場合は get ステートメントを省略することが出来ます。

定義部分

Sample

class SubscriptSample {
    let userName: String

    init(name: String){
        userName = name
    }

    // subscript の受け取る引数は [] の中で渡すもの
    subscript(action: String) -> String {
        return userName + " さん " + action
    }
}

使い方

Sample2

func test() {
    let sample = SubscriptSample(name: "happy_ryo")
    println(sample["昨夜はお楽しみでしたね"])
    println(sample["今朝もお楽しみでしたね"])
}

実行結果

Sample3

happy_ryo さん 昨夜はお楽しみでしたね // println(sample["昨夜はお楽しみでしたね"]) の出力
happy_ryo さん 今朝もお楽しみでしたね // println(sample["今朝もお楽しみでしたね"]) の出力


おまけ

読み込み専用の Subscript に対して書き込みを行おうとすると、コンパイルエラーになります。

Sample3

func writeTest() {
    let sample = SubscriptSample(name: "happy_ryo")
    sample["fuga"] = "hoge" // ここでコンパイルエラー
}

その他の特徴

引数に利用できない物がある

inout パラメータと引数のデフォルト値を利用することができません。

オーバーロードを利用することができる

オーバーロードを利用することが出来るので、引数の数や型、返り値の型によって複数の Subscript を定義する事が可能です。
引数の数や型が同一で、返り値の型が異なる Subscript が複数ある場合は、受け取り側でどの型で値を受け取るかを明示しないとコンパイルエラーになります。

受け取り側で型の指定が必要なケース

Sample

subscript(index: Int) -> Int {
    assert(userNames.count > index, "index out of range")
    return countElements(userNames[index])
}

subscript(index: Int) -> String {
    get {
        assert(userNames.count > index, "index out of range")
        return userNames[index] + " さん 昨夜はお楽しみでしたね"
    }
    set(name) {
        assert(0 > index || userNames.count >= index, "index out of range")
        userNames.insert(name, atIndex: index)
    }
}

受け取り側で型の指定が不要なケース

引数の型や数が同一でも外部名が異なれば、受け取り側での型指定は必要ない。

Sample

subscript(nameIndex index: Int) -> Int {
    assert(userNames.count > index, "index out of range")
    return countElements(userNames[index])
}

subscript(index: Int) -> String {
    get {
        assert(userNames.count > index, "index out of range")
        return userNames[index] + " さん 昨夜はお楽しみでしたね"
    }
    set(name) {
        assert(0 > index || userNames.count >= index, "index out of range")
        userNames.insert(name, atIndex: index)
    }
}

Subscript では複数の引数を受け取る事が出来る

Subscript では複数の引数を受け取ることも可能です。

Sample

class Hoge {
    subscript(index: Int, sub: Int) -> Int {
    }
}

let hoge = Hoge()
hoge[5, 4]