【Kotlin練習問題】スマートキャスト

Kotlin

スマートキャストとは

KolinではJavaのように毎回明示的にキャストをする必要はありません。コンパイラがキャスト前の変数が明らかにキャスト後の変数と一致していると判断できる場合には自動的に安全なキャストが挿入されるようになっています。これをスマートキャストと呼びます。

例えば以下のコードではxは自動的にキャストされます。

fun demo(x: Any) {
    if (x is String) {
        print(x.length) //xは自動的にStringにキャストされている
    }
}

if文の条件文の中でxがStringであることをチェックしており、Stringであればlengthを出力するというコードですが、if文のボディでは条件文がtrue、すなわちxがStringであることが確定しているので、lengthを出す際にわざわざキャストしなくてもいいということです。

逆に以下のようにStringじゃなければreturnさせていれば、その後のreturnされていないコードではxはStringなので、同様にスマートキャストが適用されます。

if (x !is String) return

print(x.length) // xは自動的にStringにキャストされている

スマートキャストはwhen文を使用したコードでも適用されます。

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

when文の一つ目ではIntである場合の処理ですので、xはIntにスマートキャストされ、x+1も問題なく処理されます。以降の文も同様です。

スマートキャストは変数のチェックとその変数が使用されるまでの間に変数の中身が変わっていないということをコンパイラが確認できる場合にのみ有効です。具体的には以下のようなに決められています。

  • valローカル変数はローカル委譲プロパティを除いて有効です。
  • valプロパティはそれがプライベート、または内部、またはそのプロパティが宣言されたモジュール内でチェックが行われていれば有効です。公開されているプロパティやカスタムゲッターをもっているプロパティに対してはスマートキャストは使えません。
  • varローカル変数はチェックと使用までの間に変更されていない場合と、その変数を変更しているラムダ式に使用されていない場合、そしてローカル委譲プロパティでない場合のみ有効です。
  • varプロパティに対しては常に無効です。varプロパティはコードのいつ、どこからでも変更される可能性があるためです。

問題

以下のJavaコードをスマートキャストとwhenを使ってKotlinで書き換えてください。

Javaコード:

public int eval(Expr expr) {
    if (expr instanceof Num) {
        return ((Num) expr).getValue();
    }
    if (expr instanceof Sum) {
        Sum sum = (Sum) expr;
        return eval(sum.getLeft()) + eval(sum.getRight());
    }
    throw new IllegalArgumentException("Unknown expression");
}

Kotlinコード:

fun eval(expr: Expr): Int =
        when (expr) {
            is Num -> TODO()
            is Sum -> TODO()
            else -> throw IllegalArgumentException("Unknown expression")
        }

interface Expr
class Num(val value: Int) : Expr
class Sum(val left: Expr, val right: Expr) : Expr

Kotlin Playgroundでコードの編集・動作確認ができます。

答え

fun eval(expr: Expr): Int =
        when (expr) {
            is Num -> expr.value
            is Sum -> eval(expr.left) + eval(expr.right)
            else -> throw IllegalArgumentException("Unknown expression")
        }

interface Expr
class Num(val value: Int) : Expr
class Sum(val left: Expr, val right: Expr) : Expr

答え

その他の問題はこちらからどうぞ。

おすすめ書籍

Kotlinの文法をまず学びたい!という方には以下の書籍がおすすめです。Kotlinは日本語書籍がまだ豊富とは言えない状況ですが、細かく解説されており、Kotlin入門者のかたでもつまずくことなく学習できると思います。

実際にアプリを作りながら覚えていきたい!という方には以下もお勧めです。はじめに上の書籍で文法をさらっと学んでから取り組むのがお勧めです。

プロフィール

プロフィール
コードラボJP

大学卒業後SEに就職、現在は退職しフリーランスとして活動中。
『初心者でも挫折せずに一人でプログラミングを学べる』をモットーに、コードラボJPを開設
お問い合わせ等はcodelabsjp@gmail.comまで

コードラボJPをフォローする
タイトルとURLをコピーしました