見出し画像

Swiftでプログラミング-Type Casting

型キャストは、インスタンスの型をチェックする方法、またはそのインスタンスをそれ自体のクラス階層内の他の場所とは異なるスーパークラスまたはサブクラスとして扱う方法です。

Swiftでの型キャストは、isおよび"as"演算子を使用して実装されます。 これらの2つの演算子は、値の型をチェックしたり、値を別の型にキャスト(変更)したりするためのシンプルで表現力豊かな方法を提供します。

型キャストを使用して、型がプロトコルに適合しているかどうかをチェックすることもできます。

Defining a Class Hierarchy for Type Casting 型キャストのクラス階層の定義

クラスとサブクラスの階層で型キャストを使用して、特定のクラスインスタンスの型を確認し、そのインスタンスを同じ階層内の別のクラスにキャストできます。 以下の3つのコードスニペットは、型キャストの例で使用するために、クラスの階層とそれらのクラスのインスタンスを含む配列を定義します。

最初のスニペットは、MediaItemと呼ばれる新しい基本クラスを定義します。 このクラスは、デジタルメディアライブラリに表示されるあらゆる種類のアイテムの基本機能を提供します。 具体的には、String型のnameプロパティと、initname初期化子を宣言します。 (すべての映画や曲を含むすべてのメディアアイテムに名前が付けられていることを前提としています。)

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

次のスニペットは、MediaItemの2つのサブクラスを定義します。 最初のサブクラスであるMovieは、映画または映画に関する追加情報をカプセル化します。 これは、対応するイニシャライザを使用して、基本MediaItemクラスの上にdirectorプロパティを追加します。 2番目のサブクラスSongは、基本クラスの上にartistプロパティと初期化子を追加します。

   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)
       }
   }

最後のスニペットは、2つのMovieインスタンスと3つのSongインスタンスを含むlibraryという定数配列を作成します。 ライブラリ配列の型は、配列リテラルの内容で初期化することによって推測されます。 Swiftのタイプチェッカーは、MovieとSongがMediaItemの共通のスーパークラスを持っていることを推測できるため、ライブラリ配列の[MediaItem]のタイプを推測します。

    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")
   ]
   // the type of "library" is inferred to be [MediaItem]

ライブラリに保存されているアイテムは、舞台裏でまだMovieとSongのインスタンスです。 ただし、この配列の内容を反復処理すると、返されるアイテムは、MovieやSongではなくMediaItemとして入力されます。 それらをネイティブタイプとして使用するには、以下で説明するように、タイプを確認するか、別のタイプにダウンキャストする必要があります。

Checking Type

タイプチェック演算子(is)を使用して、インスタンスが特定のサブクラスタイプであるかどうかをチェックします。 タイプチェック演算子は、インスタンスがそのサブクラスタイプの場合はtrueを返し、そうでない場合はfalseを返します。

以下の例では、ライブラリ配列内のMovieインスタンスとSongインスタンスの数をカウントするmovieCountとsongCountの2つの変数を定義しています。

   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"

この例では、ライブラリ配列内のすべてのアイテムを繰り返し処理します。 各パスで、for-inループは、アイテム定数を配列内の次のMediaItemに設定します。

item is Movieは、現在のMediaItemがMovieインスタンスの場合はtrueを返し、そうでない場合はfalseを返します。 同様に、item is Songは、itemがSongインスタンスであるかどうかをチェックします。 for-inループの最後で、movieCountとsongCountの値には、各タイプで検出されたMediaItemインスタンスの数が含まれます。

Downcasting

特定のクラスタイプの定数または変数は、実際には舞台裏でサブクラスのインスタンスを参照する場合があります。これが当てはまると思われる場合は、型キャスト演算子(as?またはas!)を使用してサブクラス型にダウンキャストしてみてください。

ダウンキャストは失敗する可能性があるため、型キャスト演算子には2つの異なる形式があります。条件付きフォームas?は、ダウンキャストしようとしているタイプのoptional valueを返します。強制形式は、as!として、ダウンキャストを試行し、結果を単一の複合アクションとして強制アンラップします。

ダウンキャストが成功するかどうかわからない場合は、型キャスト演算子の条件付き形式(as?)を使用します。この形式の演算子は常にoptional valueを返し、ダウンキャストが不可能な場合は値はnilになります。これにより、ダウンキャストが成功したかどうかを確認できます。

ダウンキャストが常に成功することが確実な場合にのみ、型キャスト演算子の強制形式(as!)を使用してください。この形式の演算子は、誤ったクラスタイプにダウンキャストしようとすると、ランタイムエラーをトリガーします。

以下の例では、ライブラリ内の各MediaItemを繰り返し処理し、各itemの適切な説明を出力します。これを行うには、MediaItemとしてだけでなく、実際のMovieまたはSongとして各itemにアクセスする必要があります。これは、説明で使用するために映画または曲の監督またはアーティストのプロパティにアクセスできるようにするために必要です。

この例では、配列内の各アイテムは映画の場合もあれば、歌の場合もあります。各itemに使用する実際のクラスが事前にわからないため、型キャスト演算子(as?)の条件付き形式を使用して、ループを通過するたびにダウンキャストを確認するのが適切です。

    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

この例は、現在のitemをMovieとしてダウンキャストしようとすることから始まります。 itemはMediaItemインスタンスであるため、Movieである可能性があります。同様に、それがSongである可能性もあり、単なるベースMediaItemである可能性もあります。この不確実性のために、として?型キャスト演算子の形式は、サブクラス型にダウンキャストしようとするとオプションの値を返します。 itemの結果は?MovieのタイプはMovie ?、または「optional Movie”.」です。

ライブラリ配列のSongインスタンスに適用すると、Movieへのダウンキャストは失敗します。これに対処するために、上記の例では、オプションのバインディングを使用して、オプションのMovieに実際に値が含まれているかどうかを確認します(つまり、ダウンキャストが成功したかどうかを確認します)。このオプションのバインディングは、「if let movie = item as?Movie」、次のように読むことができます:

「itemにMovieとしてアクセスしてみてください。これが成功した場合は、Movieという新しい一時定数を、返されたオプションのMovieに格納されている値に設定します。」

ダウンキャストが成功すると、movieのプロパティを使用して、そのMovieインスタンスの説明(ディレクターの名前を含む)が出力されます。同様の原則を使用して、曲のインスタンスをチェックし、ライブラリで曲が見つかった場合は常に適切な説明(アーティスト名を含む)を出力します。

キャストによって実際にインスタンスが変更されたり、その値が変更されたりすることはありません。基になるインスタンスは同じままです。キャストされたタイプのインスタンスとして扱われ、アクセスされるだけです。

Type Casting for Any and AnyObject

Swiftには、非特定のタイプを操作するための2つの特別なタイプがあります。

・Anyは、関数タイプを含め、すべてのタイプのインスタンスを表すことができます。
・AnyObjectは、任意のクラスタイプのインスタンスを表すことができます。

AnyとAnyObjectは、それらが提供する動作と機能が明示的に必要な場合にのみ使用してください。 コードで使用する予定のタイプを具体的に指定することをお勧めします。

Anyを使用して、関数タイプと非クラスタイプを含むさまざまなタイプを組み合わせて操作する例を次に示します。 この例では、thingsという名前の配列を作成します。この配列には、Any型の値を格納できます。

   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)" })

things配列には、2つのInt値、2つのDouble値、String値、タイプ(Double、Double)のタプル、Movie「Ghostbusters」、およびString値を取り別のString値を返すクロージャー式が含まれています。

Any型またはAnyObject型であることがわかっている定数または変数の特定の型を検出するには、switchステートメントの場合にisまたはasパターンを使用できます。 以下の例では、things配列内の項目を繰り返し処理し、switchステートメントを使用して各項目の型を照会します。 switchステートメントのいくつかのcaseは、一致した値を指定された型の定数にバインドして、その値を出力できるようにします。

    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タイプは、optional valueを含む、任意のタイプの値を表します。 タイプAnyの値が必要な場合にoptional valueを使用すると、Swiftは警告を表示します。 オプションの値をAny値として使用する必要がある場合は、以下に示すように、as演算子を使用してオプションをAnyに明示的にキャストできます。
   let optionalNumber: Int? = 3
   things.append(optionalNumber)        // Warning
   things.append(optionalNumber as Any) // No warning

いいなと思ったら応援しよう!