Skip to content

型チェックとキャスト

Kotlinでは、実行時にオブジェクトの型をチェックするための型チェックを実行できます。型キャストを使用すると、オブジェクトを別の型に変換できます。

ジェネリクスの型チェックとキャスト(例:List<T>Map<K,V>)について詳しく学習するには、ジェネリクスの型チェックとキャストを参照してください。

is および !is 演算子

オブジェクトが特定の型に準拠しているかどうかを実行時にチェックするには、is 演算子またはその否定形である !is を使用します。

kotlin
if (obj is String) {
    print(obj.length)
}

if (obj !is String) { // Same as !(obj is String)
    print("Not a String")
} else {
    print(obj.length)
}

スマートキャスト

ほとんどの場合、コンパイラが自動的にオブジェクトをキャストしてくれるため、明示的なキャスト演算子を使用する必要はありません。これはスマートキャストと呼ばれます。コンパイラは、不変な値に対する型チェックと明示的なキャストを追跡し、必要に応じて暗黙的な(安全な)キャストを自動的に挿入します。

kotlin
fun demo(x: Any) {
    if (x is String) {
        print(x.length) // x is automatically cast to String
    }
}

コンパイラは、否定的なチェックがリターンにつながる場合にキャストが安全であることを認識するほど賢明です。

kotlin
if (x !is String) return

print(x.length) // x is automatically cast to String

制御フロー

スマートキャストは、if 条件式だけでなく、whenwhile ループでも機能します。

kotlin
when (x) {
    is Int -> print(x + 1)
    is String -> print(x.length + 1)
    is IntArray -> print(x.sum())
}

ifwhen、またはwhileの条件で使用する前にBoolean型の変数を宣言すると、コンパイラによってその変数について収集されたすべての情報が、スマートキャストのために対応するブロック内でアクセス可能になります。

これは、ブール条件を変数に抽出するなどの場合に役立ちます。そうすることで、変数に意味のある名前を付けられ、コードの可読性が向上し、後でコード内で変数を再利用できるようになります。例:

kotlin
class Cat {
    fun purr() {
        println("Purr purr")
    }
}

fun petAnimal(animal: Any) {
    val isCat = animal is Cat
    if (isCat) {
        // The compiler can access information about
        // isCat, so it knows that animal was smart-cast
        // to the type Cat.
        // Therefore, the purr() function can be called.
        animal.purr()
    }
}

fun main(){
    val kitty = Cat()
    petAnimal(kitty)
    // Purr purr
}

論理演算子

コンパイラは、&&または||演算子の左側に型チェック(通常または否定)がある場合、右側でスマートキャストを実行できます。

kotlin
// x is automatically cast to String on the right-hand side of `||`
if (x !is String || x.length == 0) return

// x is automatically cast to String on the right-hand side of `&&`
if (x is String && x.length > 0) {
    print(x.length) // x is automatically cast to String
}

オブジェクトに対する型チェックをor演算子(||)と組み合わせると、最も近い共通のスーパータイプにスマートキャストされます。

kotlin
interface Status {
    fun signal() {}
}

interface Ok : Status
interface Postponed : Status
interface Declined : Status

fun signalCheck(signalStatus: Any) {
    if (signalStatus is Postponed || signalStatus is Declined) {
        // signalStatus is smart-cast to a common supertype Status
        signalStatus.signal()
    }
}

共通のスーパータイプは、ユニオン型近似値です。ユニオン型は現在Kotlinではサポートされていません

インライン関数

コンパイラは、インライン関数に渡されるラムダ関数内でキャプチャされた変数をスマートキャストできます。

インライン関数は、暗黙的なcallsInPlaceコントラクトを持つものとして扱われます。これは、インライン関数に渡されたラムダ関数がその場で呼び出されることを意味します。ラムダ関数はインプレースで呼び出されるため、コンパイラはラムダ関数がその関数本体内に含まれる変数の参照を漏洩させないことを認識しています。

コンパイラはこの知識と他の分析を組み合わせて、キャプチャされた変数をスマートキャストしても安全かどうかを判断します。例:

kotlin
interface Processor {
    fun process()
}

inline fun inlineAction(f: () -> Unit) = f()

fun nextProcessor(): Processor? = null

fun runProcessor(): Processor? {
    var processor: Processor? = null
    inlineAction {
        // The compiler knows that processor is a local variable and inlineAction()
        // is an inline function, so references to processor can't be leaked.
        // Therefore, it's safe to smart-cast processor.
      
        // If processor isn't null, processor is smart-cast
        if (processor != null) {
            // The compiler knows that processor isn't null, so no safe call 
            // is needed
            processor.process()
        }

        processor = nextProcessor()
    }

    return processor
}

例外処理

スマートキャスト情報はcatchブロックとfinallyブロックに引き継がれます。これにより、コンパイラがオブジェクトがNull許容型であるかどうかを追跡するため、コードの安全性が向上します。例:

kotlin
fun testString() {
    var stringInput: String? = null
    // stringInput is smart-cast to String type
    stringInput = ""
    try {
        // The compiler knows that stringInput isn't null
        println(stringInput.length)
        // 0

        // The compiler rejects previous smart cast information for 
        // stringInput. Now stringInput has the String? type.
        stringInput = null

        // Trigger an exception
        if (2 > 1) throw Exception()
        stringInput = ""
    } catch (exception: Exception) {
        // The compiler knows stringInput can be null
        // so stringInput stays nullable.
        println(stringInput?.length)
        // null
    }
}
fun main() {
    testString()
}

スマートキャストの前提条件

スマートキャストは、コンパイラが、変数とチェックとその使用の間で変数が変更されないことを保証できる場合にのみ機能することに注意してください。

スマートキャストは以下の条件で使用できます。

val ローカル変数 [ローカルデリゲートプロパティ](delegated-properties.md)を除き、常に。
val プロパティ プロパティがprivateまたはinternalである場合、またはプロパティが宣言されているのと同じ[モジュール](visibility-modifiers.md#modules)内でチェックが実行される場合。openプロパティやカスタムゲッターを持つプロパティではスマートキャストを使用できません。
var ローカル変数 変数がチェックとその使用の間で変更されず、それを変更するラムダ内でキャプチャされておらず、かつローカルデリゲートプロパティでない場合。
var プロパティ 他のコードによって変数がいつでも変更される可能性があるため、決して。

"非安全な"キャスト演算子

オブジェクトを非Null許容型に明示的にキャストするには、非安全なキャスト演算子asを使用します。

kotlin
val x: String = y as String

キャストが不可能な場合、コンパイラは例外をスローします。これが非安全と呼ばれる理由です。

前の例で、ynullの場合、上記のコードも例外をスローします。これは、StringNull許容ではないため、nullStringにキャストできないためです。Null値の可能性がある場合にも例が機能するようにするには、キャストの右側にNull許容型を使用します。

kotlin
val x: String? = y as String?

"安全な"(Null許容)キャスト演算子

例外を避けるには、失敗時にnullを返す安全なキャスト演算子as?を使用します。

kotlin
val x: String? = y as? String

as?の右側が非Null許容型Stringであるにもかかわらず、キャストの結果はNull許容型になることに注意してください。