【Swift】is, as?, as!について

# はじめに
`is`,`as?`,`as!`の使い所が合っているのか疑問に思い調べてみました。

https://docs.swift.org/swift-book/LanguageGuide/TypeCasting.html

# `is`と`as`

<details><summary> MediaItem クラス </summary>

```swift
class MediaItem {
    var name: String
    init(name: String) {
        self.name = name
    }
}
```

</details>

<details><summary> Movie/Song クラス </summary>

```swift
class Movie: MediaItem {
    var director: String
    init(name: String, director: String) {
        self.director = director
        super.init(name: name)
    }
}

class Song: MediaItem {
    var artist: String
    init(name: String, artist: String) {
        self.artist = artist
        super.init(name: name)
    }
}
```

</details>

<details><summary> library: [MediaItem] </summary>

```swift
let library = [
    Movie(name: "Casablanca", director: "Michael Curtiz"),
    Song(name: "Blue Suede Shoes", artist: "Elvis Presley"),
    Movie(name: "Citizen Kane", director: "Orson Welles"),
    Song(name: "The One And Only", artist: "Chesney Hawkes"),
    Song(name: "Never Gonna Give You Up", artist: "Rick Astley")
]
```

</details>

### `is`

あるインスタンスが特定のサブクラス型であるかどうかを調べるには、型チェック演算子 (is) を使用する。インスタンスがそのサブクラスの型であればtrueが返る。

```swift
var movieCount = 0
var songCount = 0

for item in library {
    if item is Movie {
        movieCount += 1
    } else if item is Song {
        songCount += 1
    }
}

print("Media library contains \(movieCount) movies and \(songCount) songs")
// Prints "Media library contains 2 movies and 3 songs"
```

### `as?`or`as!`

あるクラス型の定数や変数が、実は裏ではサブクラスのインスタンスを参照していることがあり、このような場合に型キャスト演算子 (as? または as!) を使用してサブクラスの型にダウンキャストすることができる。

ダウンキャストが成功するかどうかわからない場合は、`as?`
常にオプションの値を返し、ダウンキャストに失敗した場合はnilになる。これにより、ダウンキャストが成功したかどうかを確認することができる。

ダウンキャストが必ず成功すると確信できる場合は、`as!`
不正なクラス型にダウンキャストしようとして失敗すると、実行時エラーになる。


```swift
for item in library {
    if let movie = item as? Movie {
        print("Movie: \(movie.name), dir. \(movie.director)")
    } else if let song = item as? Song {
        print("Song: \(song.name), by \(song.artist)")
    }
}

// Movie: Casablanca, dir. Michael Curtiz
// Song: Blue Suede Shoes, by Elvis Presley
// Movie: Citizen Kane, dir. Orson Welles
// Song: The One And Only, by Chesney Hawkes
// Song: Never Gonna Give You Up, by Rick Astley
```

※ キャスティングは、実際にインスタンスを変更したり、その値を変更したりするものではなく、基本的なインスタンスはそのままで、単にキャストされた型のインスタンスとして扱われ、アクセスされるだけ

# `Any`と`AnyObject`

`Any`は、関数型を含む、まったく任意の型のインスタンスを表す
`AnyObject`は、任意のクラス型のインスタンスを表す

<details><summary> things: [Any] </summary>

```swift
var things: [Any] = []

things.append(0)
things.append(0.0)
things.append(42)
things.append(3.14159)
things.append("hello")
things.append((3.0, 5.0))
things.append(Movie(name: "Ghostbusters", director: "Ivan Reitman"))
things.append({ (name: String) -> String in "Hello, \(name)" })
```

Any型は、オプショナル型を含むあらゆる型の値を表すが、SwiftはAny型の値が期待される場所でオプショナル値を使用すると警告を表示するため、オプショナル値を Any 値として使用する必要がある場合、以下のようにオプショナル値を明示的に Anyにキャストするために`as`を使用する

```swift
let optionalNumber: Int? = 3
things.append(optionalNumber)        // Warning
things.append(optionalNumber as Any) // No warning
```

----

</details>

`Any`または`AnyObject`であることが分かっている定数または変数の特定の型を検出するには、switch文のcaseで`is` または`as`を使用する
```swift
for thing in things {
    switch thing {
    case 0 as Int:
        print("zero as an Int")
    case 0 as Double:
        print("zero as a Double")
    case let someInt as Int:
        print("an integer value of \(someInt)")
    case let someDouble as Double where someDouble > 0:
        print("a positive double value of \(someDouble)")
    case is Double:
        print("some other double value that I don't want to print")
    case let someString as String:
        print("a string value of \"\(someString)\"")
    case let (x, y) as (Double, Double):
        print("an (x, y) point at \(x), \(y)")
    case let movie as Movie:
        print("a movie called \(movie.name), dir. \(movie.director)")
    case let stringConverter as (String) -> String:
        print(stringConverter("Michael"))
    default:
        print("something else")
    }
}

// zero as an Int
// zero as a Double
// an integer value of 42
// a positive double value of 3.14159
// a string value of "hello"
// an (x, y) point at 3.0, 5.0
// a movie called Ghostbusters, dir. Ivan Reitman
// Hello, Michael
```



# おわりに
`Any`または`AnyObject`の値をswitchで型キャストする実装はやったことがないので、使っていけたら便利なのかもと思いました。

# 参照

https://docs.swift.org/swift-book/LanguageGuide/TypeCasting.html

https://swift.codelly.dev/guide/%E5%9E%8B%E3%82%AD%E3%83%A3%E3%82%B9%E3%83%88/#%E5%9E%8B%E3%82%AD%E3%83%A3%E3%82%B9%E3%83%88%E3%81%A8%E3%81%AF