Javaでプログラムを書いたことがあるプログラマーであれば「NullPointerException」というエラーで悩まされた方もいらっしゃるのではないでしょうか。
これは、オブジェクトに対する参照がない、つまり、nullになっている変数にアクセスするときに起こるエラーです。
このNullPointerExceptionを回避するためにはプログラム上で変数がnullになっていないか常にチェックする必要があります。
またJavaだけではなく多くのプログラム言語が変数がnullによるエラーの問題をはらんできます。
このnullアクセスの問題を回避するためにSwiftではオプショナルという仕組みを用意しています。
今回は、このSwiftのオプショナルについて以下の観点で丁寧に解説します。
- Swiftのオプショナル(?や!)とは何か
- なぜSwiftにはオプショナル(?や!)が用意されているのか
- Swiftのオプショナル(?や!)をどのように使うのか
参考本
[改訂新版]Swift実践入門 ── 直感的な文法と安全性を兼ね備えた言語 WEB+DB PRESS plus
Swiftのオプショナル(?や!)とは何か
まずSwiftの変数や定数はnilを許容しません(Swiftではnullをnilといいます)。
例えば、
と書くとnilでは初期化できないというエラーが発生します。
しかし、状況によっては変数がnilになる場合もあり得るのでnilを許容する方法を用意する必要があります。
そこで、Swiftでは型の後に?(クエスチョンマーク)を書いて「値があるかもしれないし無いかもしれない」、つまり、オプショナル(制限を受けない:任意)な値を持たせる仕組みを用意しています。
例えば、
と書くとaは「オプショナルな値を持つ文字列型の変数」となります。
つまり変数aの値はオプショナルでラップされているということです。
例えば、
print(a)
とするとOptional(“a”)と表示されます。
また、
のように変数を宣言した場合、変数には自動的にnilが設定されます。
それから、以下のようにSwingの Int 型には、String 値を Int 値に変換するイニシャライザがあります。
let convertedNumber = Int(possibleNumber)
しかし、例えば、”Hello”など、あらゆる文字列が整数に変換できるわけではありません。
なので、この場合、convertedNumber は “Int?” 型と推論されます。
このようにSwiftでは、定数または変数が、ある条件において値が存在しない状態を扱う必要がある場合、適切な型のオプショナル値として宣言する必要があります。
なぜSwiftにはオプショナル(?や!)が用意されているのか
先に述べたように多くのプログラム言語が変数がnilによるエラーの問題をはらんできます。
そこで比較的新しい言語であるSwiftはこの問題を回避するために「オプショナル」という仕組みを導入したのです。
さて、例えば、
a.count
のようにオプショナル値にアクセスしようとすると「オプショナル値をアンラップしてください」という内容のエラーが発生します。
このようにSwiftではオプショナル値をアンラップさせてから操作できるようにするという仕組みを通して変数がnilによるエラーの問題を回避しているのです。
Swiftのオプショナル(?や!)をどのように使うか
それでは具体的にオプショナルの使い方について見ていきましょう。
先ほど、
Swiftでは、定数または変数が、ある条件において値が存在しない状態を扱う必要がある場合、適切な型のオプショナル値として宣言する必要がある、
Swiftではオプショナル値をアンラップさせてから操作する
という説明をしました。
ここではオプショナル値をアンラップする以下の3つの方法について紹介します。
- 強制アンラップ
- オプショナルバインディング
- 無条件アンラップ
強制アンラップ
これは、if文で変数がnilでないことを確認した上で強制的にアンラップする方法です。
例えば、以下のようにオプショナルが値を保持していることを確認した後は、オプショナルの名前の末尾に!(エクスクラメーションマーク)を付加して値にアクセスできます。
let convertedNumber = Int(possibleNumber)
//convertedNumber は “Int?” 型です。
if convertedNumber != nil {
print(“convertedNumber has an integer value of \(convertedNumber!).”)
}
// “convertedNumber has an integer value of 123.” と出力
この!は、「このオプショナルが間違いなく値を保持していることがわかっているので、その値を使用する」ということを意味します。
これをオプショナル値の強制アンラップと呼びます。
なお、例えば、
a!.count
のように
値が存在しないオプショナル値を ! でアクセスしようとすると、実行時エラーになります。
なので、! を使ってオプショナル値を強制アンラップする前に、nil でないことを常に確認する必要があります。
オプショナルバインディング
これは、if文やwhile文で変数がnilでないことを確認した上で、その値を一時的な定数として利用できるようにする方法です。
例えば、以下のようにオプショナルが値を保持していることを確認した後は、!を使って強制的にアンラップするのではなく、その値を一時的な定数にバインドして利用できるようにします。
if let actualNumber = Int(possibleNumber) {
print(“\’\(possibleNumber)\’ has an integer value of \(actualNumber)”)
} else {
print(“\’\(possibleNumber)\’ could not be converted to an integer”)
}
// “‘123’ has an integer value of 123” と出力される
//Int(possibleNumber) はオプショナル値を保持します。
この場合、定数がオプショナルが保持している値ですでに初期化されているので、! を付けて(強制アンラップして)値にアクセスする必要はありません。
また、以下のように、1 つの if 文に複数のオプショナルバインディングを含めることができ、ブール条件をチェックするために where 節を使うことができます。
print(“\(firstNumber) < \(secondNumber)”)
}
// “4 < 42” と出力される
//Int(“4”)やInt(“42”) はオプショナル値を保持します。
無条件アンラップ
オプショナルは、定数や変数に値が存在しない場合があることを示していますが、値が設定された後など、オプショナルが常に値が持っていることがプログラムの構成から明らかな場合もあります。
こういったケースでは、常に値があることを前提にしてアクセスのたびにオプショナル値をチェックしてアンラップする処理をするのは効率がよくありません。
このような場合、以下のように、オプショナルにしたい型の直後にクエスチョンマーク (String?) を置くのではなく、エクスクラメーションマーク (String!) を置いて無条件にアンラップされるオプショナル(使用時に自動的にアンラップされることが許可されているオプショナル)を記述します。
この場合、a:String?のときと同様、変数には自動的にnilが設定されます。
以下の例の場合、通常のオプショナルなのでアクセスのたびにオプショナル値を!で強制的にアンラップする必要があります。
let forcedString: String = possibleString! // エクスクラメーションマークが必要
しかし、!を使って無条件アンラップされるオプショナルにした場合、アンラップする必要なくアクセスすることができ手間が省けます。
let implicitString: String = assumedString // エクスクラメーションマークは不要
なお、以下のように無条件にアンラップされるオプショナルが nil のときラップされている値にアクセスしようとすると、実行時エラーになります。
a.count
これは、以下のように値が存在しない通常のオプショナルに、エクスクラメーションマークを付けた場合の実行時エラーとまったく同じ結果です。
a!.count
以上より、変数がどこかで nil になり得る場合は、無条件にアンラップされるオプショナルを使用せずに通常のオプショナルを使用するようにします。
以上、今回は、Swiftのオプショナルについて解説しました。
[…] ショナルな HTMLElement として定義されています。 オプショナルについては、 Swiftのオプショナル を参照してください。 上記の例の場合、以下のようにクラスインスタンスとクロ […]