# 型キャストとは

Swiftはインスタンスの型が、あるスーパークラスのサブクラスか判定したり、または変換する仕組みを提供しています。その仕組みを型キャストと言います。型キャストは基本的に同じクラス階層のスーパークラスとサブクラスの間で行われます。

Swiftの型キャストは isas 演算子で実装されています。これら2つの演算子は、値の型をチェックしたり、値を別の型に変換することができます。

# is

is は、インスタンスが特定のサブクラス型であるかどうかを判定します。インスタンスが指定したサブクラス型の場合はtrueを返し、そうでない場合はfalseを返します。

次の例では、Button クラスをスーパークラスとして定義し、サブクラスとして RedButtonBlueButton があります。

class Button {}

class RedButton: Button {}

class BlueButton: Button {}

クラスのインスタンスを生成して、is 演算子でサブクラスのインスタンスか判定します。

let array = [RedButton(),BlueButton()]

for a in array {
  // is演算子でRedButtonのインスタンスかを判定する
  if a is RedButton {
    print("RedButton")
  }
}

/* 実行結果 */
// RedButton

実行結果は、RedButton のインスタンスのときだけ出力しているのが分かります。

ここで、Buttonクラスを継承しない GreenButton クラスを追加してみましょう。if文では、Buttonクラスを継承しているインスタンスのみ出力するように書き換えます。

class Button {}

class RedButton: Button {}

class BlueButton: Button {}

class GreenButton {}

// GreenButtonのインスタンスを追加
let array: [Any] = [RedButton(),BlueButton(), GreenButton()]

for a in array {
  // Buttonを継承しているインスタンスのみ出力する
  if a is Button {
    print("Button")
  }
}

/* 実行結果 */
// Button
// Button

実行結果を見ると、GreenButtonクラスはButtonクラスを継承していないのでfalse判定され、出力されないのが分かります。

# ダウンキャスト

クラスの継承やプロトコルの階層関係がある型どうしにおいて、下位の型に変換する操作をダウンキャストと言います。Swiftではダウンキャストするために、as?as! 演算子が提供されています。

# as?

as? はそのインスタンスが、特定のサブクラスのインスタンスか明確でない場合に使用します。返り値はOptional型を受け取り、ダウンキャストに失敗するとnilを返します。

次の例では、Buttonクラスのインスタンスとして代入した変数に対してダウンキャストをしています。ButtonクラスはRedButtonクラスと同じ階層関係に位置しているので、ダウンキャストすることができます。

class Button {}

class RedButton: Button {
  func red() -> String {
    "red"
  }
}

// Button型として代入
let a: Button = RedButton()

// RedButtonにダウンキャストする
let b = a as? RedButton

// RedButton.red() を実行することができる
b?.red()

階層関係にないクラスをダウンキャストすると、値はnilになります。

class Button {}

class RedButton: Button {
  func red() -> String {
    "red"
  }
}

class Button2 {}

let a = Button2()

// Button2 は RedButton と階層関係ではないのでダウンキャストできない
let b = a as? RedButton

b // nil

# as!

as? がOptional型を返すのに対して、as! はダウンキャストに失敗すると実行時エラーになります。

class Button {}

class RedButton: Button {}

class Button2 {}

let a = Button2()

let b = a as! RedButton

/* 実行結果 */
// Execution was interrupted, reason: EXC_BAD_INSTRUCTION

as! は、インスタンスの型が明確に分かっている時だけ使用するのがいいでしょう。

# AnyObject

AnyObject 型 は、どのクラスのインスタンスにも適用することができます。

次の例では、AnyObject型の配列に複数のクラスのインスタンスを代入しています。

class Button {}
class RedButton: Button {}
class Button2 {}
class Button3 {}

let array: [AnyObject] = [
  Button(),
  RedButton(),
  Button2(),
  Button3()
]

# Any

Any 型は全ての型を表現することができます。

次の例では、Any型の配列に複数の型の値を代入しています。

class Button {}
func method() -> String { "hello" }
struct Person {}

let array: [Any] = [
  1,
  0.1,
  "string",
  Button(),
  method,
  Person()
]

特定の型を見分けるために、switch 文を使ってみましょう。switch文の中で asis 演算子を使うと型を判定することができます。

class Button {}
func method() -> String { "hello" }
struct Person {}

let array: [Any] = [
  1,
  0.1,
  "string",
  Button(),
  method,
  Person()
]

for a in array {
  switch a {
  case is Int:
    print(1)
  case let element as Double:
    print(element)
  case let element as String:
    print(element)
  case let element as Button:
    print(element)
  case let element as () -> String:
    print(element)
  default:
    print("不明な型")
  }
}

/* 実行結果 */
// 1
// 0.1
// string
// __lldb_expr_100.Button
// (Function)
// 不明な型