\
2014年08月26日
Posted by 屋台ブルー at 2014年08月26日00:00 Comment(0)

Swiftで遊ぼう! - 47 タイプキャスティング(Type Casting) まとめ

swift_logo2014年11月15日にブログタイトル変更:旧タイトル「Swiftで遊ぼう! - 47 アプリを作ってみよう3 Type Casting?」タイプキャストのまとめページに変更中...

iOSアプリの「Tip Calculator」を作っている真っ最中なのに、どうしてタイプキャストの話題になるのか不思議に思う人もいるでしょう。チュートリアルに沿って勉強をしていると、まだ知らない概念にぶち当たる。だって、教科書全部読めてませんから(^^;) 知らない「AnyObject」が出てきたので、これを調べるために教科書に戻ったんです。タイプキャスティングの一部のようなので、まだ読んでいないタイプキャストの勉強をするというわけ。

「AnyObject」の項目はタイプキャストの説明にでてくる。クラス継承時のタイプの考え方...

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

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

ベースになるクラスは「MediaItem」でありそれを継承して、2つのサブクラス、「Movie」と「Song」が作られている。この2つのクラスは「MediaItem」という共通のクラスを継承している。

let library = [
Movie(name: "Star Wars", director: "George Lucas"),
Song(name: "Candle In The Wind", artist: "Elton John"),
Movie(name: "Fandango", director: "Kavin Reynolds"),
Song(name: "Layla", artist: "Eric Clapton"),
Song(name: "God Is Ice", artist: "Lenny Kravitz")
]

そして、次は定数の「library」が出てくる。一瞬、何の疑問もなく頭に入ってきたけど、それは無知だったからだ。説明文を読み出すと、「あれ?」になり、再び「library」をじっくり考えなければならなくなった。

最初に並んでいる、「MediaItem」「Movie」「Song」はクラス宣言であり、ここではまだ実体(インスタンス)が無い。次の「library」はインスタンスである。「let」という定数のシンタックスがついているからだ。じゃあどんなタイプかよく見ると、[A, B, C, ....] なんで、アレー型ですよね。今までの言語を知らないから凄いのかどうか分からないんですが、Swiftには強力な推測機能がある。タイプを明示しなくても、内容からタイプを推測してくれる。

しかし、このアレー型libraryインスタンスの中身、アイテムですが(←この言葉の使い方正しいのだろうか?)、アレーのインスタンス生成時に、MovieクラスとSongクラスのインスタンス生成の宣言をしているアイテムということは...むむむ、ややこしいね(^^;) タイプは、Movieクラス、Songクラスが混在しているように思えます。しかし、実はそうじゃないという説明がここでされているんです。

それぞれのアイテムのタイプは、共通の継承クラスである「MdeiaItem」ということになる。じゃあ、このアレー型の「library」インスタンスを生成するときに明示的にしてやると次のようになる。

let library: Array<MediaItem> = [
Movie(name: "Star Wars", director: "George Lucas"),
Song(name: "Candle In The Wind", artist: "Elton John"),
Movie(name: "Fandango", director: "Kavin Reynolds"),
Song(name: "Layla", artist: "Eric Clapton"),
Song(name: "God Is Ice", artist: "Lenny Kravitz")
]

もしくは、

let library: [MediaItem] = [
Movie(name: "Star Wars", director: "George Lucas"),
Song(name: "Candle In The Wind", artist: "Elton John"),
Movie(name: "Fandango", director: "Kavin Reynolds"),
Song(name: "Layla", artist: "Eric Clapton"),
Song(name: "God Is Ice", artist: "Lenny Kravitz")
]

まあ、この生成方法は復習ということでいいのですが、inferred(推測)によってタイプが勝手に指定されてしまうと、プログラムを書いているこっちが分かりにくくなる。ということで、このタイプを確認する方法と、タイプを変更する方法がある。

それが、「is」と「as」だ。

これの使い方だが、どちらもBoolean型として使うことが基本らしい。

var movieCount: Int = 0
var songCount = 0

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

itemというキーワードはアレー型の中身の指定になり、item is Movieという文は、itemのタイプがMovieだったら「true」を返して、違ったら「false」を返すというお約束ごと。

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

そして、「as」はダウンキャストと呼ばれる機能で、使い方は2通り、「as」と「as?」だ。まあ基本的にオプショナルをつけた「as?」が一般的である。どうしてかというと、オプショナルにすると、Boolean型に変化するという便利な機能がある。

「 if let movie = item as? Movie { ..... .do something ..... } 」という文を、私なりに解釈すると、
『アレー型コンテンツの「item(アイテム)」にMovieクラスとして(as)アクセスできるかどうか試してみなさい。もし、成功したら、オプショナルなMovieタイプの値を、定数movieに新しく設定しなさい。そして、この値はオプショナルなMovieタイプなので、値を保持しながら「true」を返して、{}内を実行しなさい』とうコントロールフロー。

もう少し「as」について考察を加えてみよう。

タイプが混在するケースというのは、タイプが変化するタイプ、そうですよね。それは継承ができる「クラス」しかないんじゃないかな? まだ、理解していない「Extension」で拡張されるタイプってのはどうなんだろう? やっぱりダウンキャストという手法が必要なのかな?

まあ、今のところ継承できるクラスに関するタイプキャストという考え方でいいのかもしれない。

アレー型のlibraryというインスタンスはMediaItemタイプを宣言しているけど、実際それぞれのアイテムはMovieタイプとSongタイプであって、アイテムを抽出して使用しようとしてもMdeiaItemタイプでは使えない。そこで、「as」を使って、それぞれのタイプにダウンキャストしてやるということ。

「as」として使うと、そのタイプが存在しない時にエラーが生じてしまうため。必ずそのタイプの存在が分かっている状態でないと使わない方がいいということ。

let library: [MediaItem] = [
Movie(name: "Star Wars", director: "George Lucas"),
Song(name: "Candle In The Wind", artist: "Elton John"),
Movie(name: "Fandango", director: "Kavin Reynolds"),
Song(name: "Layla", artist: "Eric Clapton"),
Song(name: "God Is Ice", artist: "Lenny Kravitz")
]

var movieCount: Int = 0
var songCount = 0

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

昨日の例文を使って説明しよう。上のように「is」はアイテムがそのタイプかどうか判断してboolを返すというタイプチェック機能なので、「true」を返せば、その値は必ず存在するということになり、こういう場合は「as」単独で使用することができるから以下のように書ける。

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

こういった「is」と「as」の組み合わせを同時にできるのが、「as?」と理解したらいんでしょうね。下記のほうが明らかにすっきりしている。

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


同じカテゴリー(Swiftで遊ぼう!)の記事画像
Swiftで遊ぼう! - プログラミングまとめ(ときどき更新)
Swiftで遊ぼう! - 番外編 - Xcode6:ショートカットと用語説明
Swiftで遊ぼう! - 209 - ルートビューのコードは続く:CGRect
Swiftで遊ぼう! - 208 - ルートビューのコード
Swiftで遊ぼう! - 207 - ツールバーボタンとビューコントローラーを繋ぐ
Swiftで遊ぼう! - 206 - ビューコントローラーにビューコントローラーを載せる
同じカテゴリー(Swiftで遊ぼう!)の記事
 Swiftで遊ぼう! - プログラミングまとめ(ときどき更新) (2021-01-31 00:00)
 Swiftで遊ぼう! - 番外編 - Xcode6:ショートカットと用語説明 (2015-04-05 00:00)
 Swiftで遊ぼう! - 209 - ルートビューのコードは続く:CGRect (2015-02-06 00:00)
 Swiftで遊ぼう! - 208 - ルートビューのコード (2015-02-05 00:00)
 Swiftで遊ぼう! - 207 - ツールバーボタンとビューコントローラーを繋ぐ (2015-02-04 00:00)
 Swiftで遊ぼう! - 206 - ビューコントローラーにビューコントローラーを載せる (2015-02-03 00:00)

上の画像に書かれている文字を入力して下さい
 
<ご注意>
書き込まれた内容は公開され、ブログの持ち主だけが削除できます。

削除
Swiftで遊ぼう! - 47 タイプキャスティング(Type Casting) まとめ
    コメント(0)