Kotlinの型チェックについて

採用はこちら

Kotlinにおける型チェックは、単なる「instanceofの代替」ではありません。

Kotlinは、安全性・可読性・保守性を高めるために、型チェックと型推論を密接に連携させる設計を採用しています。

本記事では、Kotlinの型チェックについて、

  • 基本構文
  • スマートキャストが成立する条件
  • 安全キャストと例外の扱い
  • null・ジェネリクスとの関係
  • 実務で安全な書き方

という観点から、誤解が生じないよう正確に解説します。

目次

Kotlinにおける型チェックとは

型チェックとは、

ある値が、指定した型のインスタンスであるかを判定すること

です。

Kotlinではこの判定結果をもとに、コンパイラが安全だと判断できる場合に限り、自動的に型を絞り込む(スマートキャスト)という点が最大の特徴です。

is 演算子による型チェック(基本)

Kotlinでは is 演算子を使って型を判定します。

val obj: Any = "Hello"

if (obj is String) {
    println(obj.length)
}

特徴

  • Javaの instanceof に相当
  • 実行時に型を判定する
  • 条件式が真の場合、後続処理でスマートキャストが発生する

スマートキャストとは何か

スマートキャストの概要

スマートキャストとは、

型チェック後、明示的なキャストを書かなくても、その型として扱える仕組み

です。

fun printLength(obj: Any) {
    if (obj is String) {
        // obj は String として扱われる
        println(obj.length)
    }
}

Javaではキャストが必須ですが、Kotlinでは不要です。

スマートキャストが成立する条件

スマートキャストは 常に使えるわけではありません

成立条件は次の一点に集約されます。

コンパイラが「この値は、この後も同じ型のままである」と保証できること

スマートキャストが成立しやすい例

if (obj is String) {
    println(obj.length)
}
  • ローカル変数
  • チェック後に再代入されていない
  • カスタム getter を持たない

スマートキャストが成立しない典型例

class Sample {
    var value: Any = "Hello"
}

if (sample.value is String) {
    println(sample.value.length) // コンパイルエラー
}

理由

  • valuevar
  • getter を通じて取得される
  • コンパイラが「同じ値が返り続ける」と証明できない

ここで重要なのは、スレッドの有無ではなく「変更可能性を否定できないこと」です。

!is 演算子とガード節

型チェックの否定には !is を使います。

if (obj !is String) return
println(obj.length)

この書き方は ガード節 と呼ばれ、以降の処理で型が確定していることを明示できるため、実務で非常によく使われます。

明示的キャスト:asas?

危険なキャスト:as

val obj: Any = "Hello"
val str: String = obj as String
  • キャスト不能 → ClassCastException
  • Javaのキャストと同等
  • 制御フローに使うべきではない

安全なキャスト:as?

val obj: Any = 123
val str: String? = obj as? String

挙動は次の通りです。

  • キャスト成功 → 対象型の値
  • キャスト失敗 → null
  • 左辺が nullnull
val length = (obj as? String)?.length

例外を発生させずに処理できるため、実務では as? が推奨されます。

when 式と型チェック

when と型チェックの組み合わせは、Kotlinらしい代表的な書き方です。

fun handle(obj: Any) {
    when (obj) {
        is String -> println(obj.length)
        is Int -> println(obj + 1)
        is List<*> -> println(obj.size)
        else -> println("Unknown")
    }
}

ポイント

  • 各分岐でスマートキャストが有効
  • 可読性が高い
  • 型ごとの処理が明確

null と型チェックの関係

nullチェックとスマートキャスト

val text: String? = "Hello"

if (text != null) {
    println(text.length)
}

null チェック後もスマートキャストが働きます。

is と null

val text: String? = null

println(text is String)   // false
println(text is String?)  // true
  • null is String → false
  • null is String? → true(nullable型に含まれるため)

ジェネリクスと型チェック

JVMでは 型消去 が行われるため、次のようなチェックはできません。

if (obj is List<String>) { // コンパイルエラー
}

回避策①:スター投影

if (obj is List<*>) {
    println(obj.size)
}

回避策②:reified 型パラメータ

inline fun <reified T> isType(value: Any): Boolean {
    return value is T
}

型チェックとパフォーマンスについて

  • iswhen は通常問題にならない
  • 例外(as の失敗)を制御フローに使うのは避ける
  • 安全キャスト+null処理が最も安定

パフォーマンスよりも 安全性・可読性を優先するのがKotlinの設計思想です。

実務で安全な定型パターン

val str = obj as? String ?: return
println(str.length)
when (obj) {
    is String -> ...
    is Int -> ...
    else -> ...
}
if (obj is String && obj.isNotEmpty()) {
    ...
}

まとめ

Kotlinの型チェックは、

  • is による安全な判定
  • コンパイラによるスマートキャスト
  • as? による例外回避
  • null・ジェネリクスとの一貫した設計

によって、「安全に書けて、読みやすく、壊れにくいコード」を実現します。

単に書き方を覚えるのではなく、

「なぜスマートキャストが効くのか/効かないのか」

を理解すると、Kotlinコードの品質が一段上がります。

以上、Kotlinの型チェックについてでした。

最後までお読みいただき、ありがとうございました。

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!
目次