LoginSignup
3
6

More than 3 years have passed since last update.

配列で学ぶOptional(配列でnilを表現する)

Posted at

この記事Optional初心者向けです。

  • Javaなどのnil(null)を知っている
  • 配列を知っている
  • Optionalはよくわからない

という方を対象にしています。
nil(何もないこと)を配列で表現する例と、それに対応するOptionalで表現する例を読むことで「Optional難しくないね」と感じてもらえればと思います。

ないことを配列で表現する

class User {
    var id: String

    init(id: String) {
        self.id = id
    }

    func showId() {
        print(id)
    }
}

このUserインスタンスがあるかもしれない、ないかもしれない…

そんな状態をnil以外の方法で表現してみましょう。
例えば配列を以下のように使えば実現できそうです。

  • 配列の要素が1つならある(nilではない)
  • 配列が空ならない(nil)
// 値がある場合
let userOrNil: Array<User> = [User(id: "id")]
// 値がない場合
let userOrNil: Array<User> = []

上記ではArray<User>と書いていますが同じ意味の[User]を使うことがほとんどですので、以降は[User]と書きます。

配列はUserではない

これがとても重要です。
当たり前のことですが、配列とUserは違うものです。

userOrNil.id // コンパイルエラーになる

コンパイルエラーになるので、nilに対してメソッドを呼び出せず、ぬるぽになりません!

値のあるなしを判定する

配列の要素数を数えれば値のあるなしを判定できます。

// 値がある場合
if userOrNil.count == 1 {
    print("値がある")
}
// 値がない場合
if userOrNil.count == 0 {
    print("値がない")
}

Userがあればidを表示し、なければ"no user"を表示してみます。

if userOrNil.count == 1 {
    let user = userOrNil[0]
    print(user.id)
} else {
    print("no user")
}

UserがあればshowId()を呼び出し、なければ何もしない場合以下のようになります。

if userOrNil.count == 1 {
    let user = userOrNil[0]
    user.showId()
}

Userがあることが確実であれば、条件判定せずいきなり呼び出すこともできます。
ただし値がなければクラッシュします。

userOrNil[0].showId()

値を取り出す

Userのid取得したいのですが、Userがあるかはわかりません。
そこで、userがあればidを含む配列、userがなければ空配列を結果とすることにします

let idOrNil: [String]
if userOrNil.count == 1 {
    let user = userOrNil[0]
    idOrNil = [user.id]
} else {
    idOrNil = []
}

Userが確実にあるとわかっているなら、配列に格納せず文字列で結果を取得しましょう。
ただし値がなければクラッシュします。

let id: String
id = userOrNil[0].id

Userがない場合、idを空文字列として扱えばいいという場合もよくあります。
この場合も配列に格納せず文字列で結果を取得しましょう。

let id: String
if userOrNil.count == 1 {
    let user = userOrNil[0]
    id = user.id
} else {
    id = ""
}

ないことをOptionalで表現する

配列を使って「ない」ことを表現することができました。

次は配列ではなくOptionalで「ない」を表現します。
Optionalは

  • 配列のように値を格納できる
  • 配列と違い1個しか格納できない
  • 配列より便利に書ける

ようなものと考えると理解しやすいです。

// 値がある場合
let userOrNil: Optional<User> = User(id: "id")
// 値がない場合
let userOrNil: Optional<User> = nil

配列の場合はArray<User>でしたが、Optionalの場合はOptional<User>となります。
ただ配列を[User]で表すように、OptionalはUser?と書くことがほとんどですので、以降はこちらの書き方を使います。

追加要素

配列版の場合、Userを配列に格納するコードを書いていました。
しかしOptional版ではそのコードはありません。
Optionalの変数(定数)に通常の値を代入すると、自動でOptionalに包んでくれる機能があるためです。
値がある場合のコードは、以下のコードと同等です。

let userOrNil: Optional<User> = User?(User(id: "id"))

OptionalはUserではない

これがとても重要です。
当たり前ですが(そうでもないかも)、OptionalとUserは違うものです。

userOrNil.id // エラーになる

コンパイルエラーになるので、nilに対してメソッドを呼び出せず、ぬるぽになりません!

値のあるなしを判定する

値のあるなしは以下のように判定します。

// 値がある場合
if userOrNil != nil {
    print("値がある")
}
// 値がない場合
if userOrNil == nil {
    print("値がない")
}

nilを使ってるじゃんというツッコミがありそうですが、nilという単語はあります。
メソッドを呼び出すとクラッシュするnilではないので許してください🙇‍♂️

Userがあればidを表示し、なければ"no user"を表示してみます。

if let user = userOrNil {
    print(user.id)
} else {
    print("no user")
}

UserがあればshowId()を呼び出し、なければ何もしない場合以下のようになります。

if let user = userOrNil {
    user.showId()
}

配列版では、要素があるか確認して、取り出して使用しました。
Optionalでは確認と取り出しを一度に行う if let var = optional という書き方ができます。

Userがあることが確実であれば、以下のように書けます。
!で値を取り出し(配列版の[0]と同等)、メソッドを呼び出します。
値がなければクラッシュします。

userOrNil!.showId()

追加要素

配列版ではなかった要素として
値があれば取り出して実行
値がなければ何もしない
を簡単に書く方法があります。

userOrNil?.showId()

処理内容はif letで値を取り出しshowId()を呼び出すコードと同じです。

値を取り出す

Userのid取得したいのですが、Userがあるかはわかりません。
そこでuserがあればidをOptionalに入れ、userがなければ(Optionalの)nilにしましょう。

let idOrNil: String?
if let user = userOrNil {
    idOrNil = user.id
} else {
    idOrNil = nil
}

Userが確実にあるとわかっているなら、通常の文字列で結果を取得しましょう。
ただし値がなければクラッシュします。

let id: String
id = userOrNil!.id

Userがない場合、idを空文字列として扱えばいいという場合もよくあります。
この場合も通常の文字列で結果を取得しましょう。

let id: String
if let user = userOrNil {
    id = user.id
} else {
    id = ""
}

?:演算子のような??演算子を使うと、上記の処理はもっと簡単に書けます。

let id: String
id = userOrNil?.id ?? ""

追加要素

配列版ではなかった要素として
値があれば取り出す
値がなければnilになる
を簡単に書く方法があります。

let idOrNil: String?
idOrNil = userOrNil?.id

処理内容はif letで値を取り出し、user.idまたはnilを代入するコードと同じです。

補足

let userOrNil: User? などと書いていますが、Optionalの変数にOptionalとわかる命名をする慣習はないです。
通常はlet user: User?のように書きます。
また以下のように名前が被っても問題ないので書き方で困ることもないはずです。

if let user = user {
    print(user.id) // このuserはOptionalではない
}

あとがき

慣れていないうちは、
OptionalString(などの中身)ではないからメソッドを呼び出せないことを忘れたり、
何か特別なすごい機能と身構えたり、
そんなところで難しいと思ってしまう人を見かけました。

この記事でOptionalに対する気負いが減ることを願っています🙏

3
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
6