さんざんいろいろな方が書いていますが、SwiftのOptionalについてです。
(Qiita初投稿です)
###Optionalとは
Swiftは"Type Safety"な言語です。と、アップルのドキュメントには書かれています。
String型にInt型の代入はできませんし、String型などの通常の型にnil
を代入することもできません。
以下のようなコードは静的な型チェックでエラーとなります。
var someValue: Int = nil //エラー
ただし、値がない変数を使用することはかなりの頻度でありますよね。そのような場合に登場するのがOptionalです。変数の型をOptionalにすることでnil
の代入が可能になります。
Optionalを指定する方法ですが、変数の宣言時に型の後ろに?
をつけます。これでnil
の代入が可能になります。
var someValue: Int? = nil //問題なし
このように型をOptionalにすることを“Optional型にラップする”と呼びます。
(このようなOptional型に“ラップする”とは、アップルのドキュメント内では述べられていませんが、Optionalの解除にUnwrapという言葉を使用しているため、この言葉を使用しました。WikipediaのSwiftのページでもこの言葉使われています。)
ちなみに、Optional型の変数や定数に初期値を指定しなかった場合、初期値は自動的にnil
になります。
var someValue: Int? //nilがデフォルトで設定される
Optional型を使用するのは”値が入っていない/入らないかもしれない”変数を宣言する場合です。Optionalという言葉そのまま、オプションな変数を指定という考え方ですね。
###Forced Unwrapping
先ほど、「通常の型にnil
を代入することはできない」と述べました。
一方で「Optional型にするとnil
の代入が可能になる」とも述べました。
それでは、Optional型の変数(=nil
が入っているかもしれない)を通常の型(=nil
の代入ができない)の変数に代入することはできるのでしょうか?
以下のコードで見てみましょう。
var number: Int? = 42
var newNumber: Int = number
一見、問題ないように見えますが、これは静的な型チェックでエラーとなります。
なぜかと言うと、number
はnil
が入っているかもしれない変数であり、newNumber
はnil
を代入することができない変数だからです。
このような場合、Optionalな変数の末尾に!
をつけることでOptional型をアンラップすることができます。
先ほどのnumber
変数に!
をつけてみましょう。これでエラーは出なくなります。
var number: Int? = 42
var newNumber: Int = number!
!
は「ここには値が入っているからOptionalを外したぞ!」と明示するということですが、この値の保証はプログラマが担保するものと思ってよいでしょう。
このように!
を使用したOptional型のアンラップは、アップルのドキュメントでは"Forced Unwrapping"と書かれています。"強制解除"という訳し方になるでしょうか。
少しお行儀が悪いので、if文でnil
チェックを行ってから使用しましょう。
var number: Int? = 42
var newNumber: Int
if number {
newNumber = number!
}else{
println("There isn't value")
}
Optionalな変数をif文に渡すと値が入っていればtrue
、値がnil
の場合はfalse
と判定されます。きちんとnumber変数がnil
でないかをチェックしているので比較的安心して使用することができますね。
このように!
=アンラップする際はOptionalがnil
でないかを必ず確認しましょう。なぜでしょうか?
以下のコードをご覧ください。
var number: Int? = nil
var newNumber: Int = number!
Optional型をアンラップしているため、このコードは静的な型チェックが働きません。しかしランタイムエラーとなってしまいます。これは非常に危険ですよね。
このような場合、プログラマがnil
でないことを保証する必要があります。
とは言え、よほどスコープの狭い変数以外は完璧に保証することは難しいですよね。ですので!
を使用する際はif文によるnil
チェックを必ず行いましょう。
###Optional Binding
if文によるチェックの方法をさらに安全にさせたものに"Optional Binding"というものがあります。
var number: Int? = 42
var newNumber: Int
if let validNumber = number {
newNumber = validNumber
}else{
println("There isn't value")
}
Optionalな変数のnil
チェックを行った後に、一時的な変数あるいは定数を使用する方法です。
validNumber
にはnil
でないことが確認された上で値が代入されています。!
によるOptional型のアンラップも使用されておらず安全に使用できます。
なるべくOptional Bindingを使用して、ランタイムエラーを引き起こす可能性もある!
によるOptional型のアンラップは使用しない方が良いのではないかと思います。
("Optional Binding"は日本語のWikipediaのSwiftでは"Optional束縛"と書かれています。この表現はニュアンスは伝わりやすい気がします。)
###関数の戻り値にOptionalを使用する
最後に関数の戻り値がOptionalだった場合はどうなのか見てみましょう。
func helloLanguage(name: String) -> String?{
if name == "Swift" {
return "Hello " + name
}else{
return nil
}
}
Optional型のString?が戻り値の型です。nil
が返ってくる可能性がありますね。
helloLanguage
から返ってきた値は通常の型の変数には代入できません。戻り値がOptional型で、nil
が返ってくる可能性があります。
var hello: String = helloLanguage("Swift") //エラー
nil
でない値が返ってくることを保証できるのであれば、以下のように!
によるOptional型のアンラップも使用できます。
var hello: String = helloLanguage("Swift")!
ただこのような場合はOptional Bindingなどでnil
チェックをした方が安全でしょう。
以下のようにhelloLanguage
関数から返ってきた値を代入する変数もOptional型であれば問題ありません。
var hello: String? = helloLanguage("Swift")
「どうしてもhello
はOptional型ではダメなんだ!」という場合は、helloLanguage
関数の方を修正(戻り値の型をOptionalでない通常の型に)する方がSwiftの"Type Safety"な言語仕様を守ることができるのではないかと思います。
###まとめ
「通常の型にnil
は代入できない」、そして「Optional型はnil
の代入が可能」であるということがポイントです。
さらに、
- if文やOptional Bindingで
nil
のチェックを行う -
!
によるOptional型のアンラップは極力使用しない
ということがOptionalを安全に使用して、Swiftの"Type Safety"な言語仕様を生かす方法だと思います。