オプショナル型

オプショナル型とは、変数の型がもつ通常の値に加えて、空の(値が無い)状態を保持できる変数です。空の状態はnilで表現します。

Apple社のSwiftの解説では、"optional type"、又は、"optional value"と書かれています。ここではオプショナル型としていますが、公式な呼び方ではありません。

宣言

オプショナル型は、変数の宣言時に、型の後ろに「?」をつけて宣言します。


var name: String?
name = nil

// オプショナル型でない変数にはnilを代入できない。
var name: String
name = nil  // エラーになる。

オプショナル型は宣言時に値を代入しない場合は自動的にnilの値が設定されています。そのため宣言時にnilで初期化する必要はありません。

オプショナルバインディング

オプショナル型は、次の様にif文の条件で使用することができます。値が空(nil)の場合はfalse、それ以外はtrueと判定されます。


var price: Int? = 100
if let p = price {
    print("価格:\(p)円")
} else {
    print("価格:未定")
}
var price: Int? = 100

if var p = price {
    p -= 10
    print("割引価格:\(p)円")
}

これをオプショナルバインディングと呼びます。

アンラップ

オプショナル型を計算で使用したり、通常の型を受け取る関数の引数に渡したりする場合は、値が空でないことを明示するために、アンラップする必要があります。アンラップはラップ(包装)の反対で、オプションという包装紙に包まれた変数の包装を解くイメージです。

unwrap.png

アンラップするには、オプショナル型の変数名の後ろに「!」をつけます。


var price: Int?
price = 300
println("税込み価格は\(Int(Double(price!) * 1.08))円")   // 変数名の後ろに!をつけてアンラップ

// 非オプショナル型を受けとるには関数の呼び出し側でアンラップが必要
func calculateTax(price: Int) -> Int {
    return Int(Double(price) * 1.08)
}
var price: Int?
price = 300
let priceWithTax = calculateTax(price!)

中身が空(nil)のオプショナル型の変数をアンラップすると実行時エラーが発生します。


var price: Int?
println("価格は\(price!)円")   // エラー

if文を使うと、値が空かどうかの判定とアンラップされた別変数への代入を同時に行うことができます。


var price: Double?
:
if let unwrappedPrice = price {
    // unwrappedPriceはアンラップされているので後ろに!は不要
    println("税込み価格は\(Int(unwrappedPrice * 1.08))円")
} else {
    println("価格は未定")
}

if文の条件には結果がBool型となる条件しか書けませんが、上の書き方の場合、オプショナル型の変数が空の場合false、値が入っている場合trueと判定されif文で使用することができます。値が0の場合でも空(nil)ではないのでtrueと判定されます。

定数でなく変数を使っても同様に書けます。


var price: Double?
:
if var unwrappedPrice = price {
    unwrappedPrice *= 1.08
    println("税込み価格は\(Int(unwrappedPrice))円")
} else {
    println("価格は未定")
}

配列やディクショナリも同様にオプショナル型として宣言できます。


var party: Array<String>?
party = ["戦士", "魔法使い"]
party!.append("僧侶")

var items: Dictionary<String, Int>?
items = ["りんご": 100, "みかん": 300]
items!["いちご"] = 350

暗黙的なオプショナル型

オプショナル型にはもう1つの宣言の仕方があります。暗黙的なオプショナル型と呼ばれるもので、宣言時に?ではなく!をつけて宣言します。


var name: String!

暗黙的なオプショナル型は、空値(nil)をとり得るという意味では、上で説明したオプショナル型と同じですが、使用時にアンラップする必要がありません。

アンラップの必要がないので、初期値がnilでも使用時までには必ず値が入っていると想定されるケースでは、暗黙的なオプショナル型の方が便利です。

但し、アンラップが必要な状況で、暗黙的なオプショナル型を空のまま使用すると実行時エラーが発生します。


var price: Double!
price = 300
println("税込み価格は\(Int(price * 1.08))円")   // 変数名の後ろに!をつける必要がない
price = nil
println("税込み価格は\(Int(price * 1.08))円")   // エラー

暗黙的なオプショナル型の宣言に使用する!と、アンラップに使用する!を混同しないようにしてください。同じ記号が使われていますが意味は異なります。

暗黙的なオプショナル型もif文の中で条件として使用することができます。


var price: Double!
price = 100
if let unwrappedPrice = price {
    println("税込み価格は\(Int(unwrappedPrice * 1.08))円")
} else {
    println("価格は未定")
}

暗黙的なオプショナル型は、Cocoaのクラスやプロトコルのメソッド宣言等で多用されています。例えば、UITableViewDataSouceでは次のようなメソッドが宣言されています。


func tableView(tableView: UITableView!, cellForRowAtIndexPath indexPath: NSIndexPath!) -> UITableViewCell!

nil結合演算子

??という演算子を使うと、オプショナル型がnilでなければその値を、nilの場合は指定した値を与えることができます。
次の様に使用します。


var a: Int?
let b = a ?? 10 // aはnilなので、10が代入される

var s: String? = "Hello"
println(s ?? "Bye") //  sはnilでないので、その値(Hello)が出力される