Kotlin 2.0ではSmart castが賢くなっているので、簡単に紹介します。2.0未満ではSmart castが効いてない部分も2.0からは効くようになってたりしています。
注意: 2.0.0-RC1 で検証しています。
ローカル変数の条件
以下のようなパターンで改善されています。
fun sample1(obj: Any) {
val isString = obj is String
if (isString) {
// StringにCastされる
println(obj.length)
}
}
これまで、if (obj is String) {}
のような if
文の中にある条件式ではSmart castが効いてましたが、外にある条件では効いてませんでした。
これが2.0からはSmart castが可能になっています。 if
だけではなく when
や while
でも可能になっています。
ORによる型チェック
次は ||
を使ったときの型チェックによるSmart castが可能になっています。
interface Animal {
fun bark()
}
class Dog : Animal {
override fun bark() { println("bow wow") }
}
class Cat : Animal {
override fun bark() { println("meow") }
}
fun sample2(obj : Any) {
if (obj is Dog || obj is Cat) {
// 2.0ではAnimalにCastされる。
// 2.0未満ではAnyになるのでコンパイルエラー
obj.bark()
}
}
||
を使っていない if (obj is Dog) {}
だけでだとSmart castが可能でしたが、 ||
を使っている場合は出来ませんでした。これが2.0からは可能になります。
inline関数
inline
関数の扱いについても改善されています。
inline fun inlineFunction(block: () -> Unit) = block()
fun sample3() {
var num: Int? = null
inlineFunction {
if (num != null) {
// 2.0未満はコンパイルエラー、2.0ではOK
num ++
} else {
num = 0
}
}
}
inline
関数はその場で呼び出されるため、それを2.0から認識できるようになったようです。
関数のプロパティ
クラスが保持しているプロパティについての扱いです。
class Sample(val callback: (() -> Unit)?) {
fun run() {
if (callback != null) {
// 2.0未満では、invokeをつける必要がある
// 2.0ではそのまま呼び出せる
callback()
}
}
}
2.0未満では invoke
を使って呼び出せば問題なく使用できましたが、2.0からは invoke
がなくてもそのまま呼び出すことが可能になっています。
catch文
try catchにおけるSmart castの改善です。
fun sample5() {
var string: String? = null
string = ""
try {
println(string.length)
string = null // nullに戻す
error("Sample") // エラーを起こす
} catch (e: Exception) {
// 2.0未満ではビルドが通り、クラッシュしてしまう
// 2.0ではnullの可能性がわかってるので、ビルドが通らない
// println(string.length)
// 2.0ではsafe callの必要がある
println(string?.length)
}
}
catch
と finally
にSmart castの情報を渡せるようになったとこので、より安全なコードを書けるようになっています。
インクリメントとデクリメント
インクリメントとデクリメントしたときのSmart castが可能になっています。
interface Hoge {
operator fun inc(): Foo = Foo()
}
class Foo : Hoge {
fun run() = Unit
}
interface Piyo {
fun exec() = Unit
}
fun sample6(obj: Hoge) {
var unknownObj = obj
if (obj is Piyo) {
// 元の型がHogeなので、incが呼べる
unknownObj++
// incが呼べるということは、実態がFooであることが確定するのでメソッドが呼べる
// 2.0未満ではここでコンパイルエラーになる
unknownObj.run()
// 上記の流れからPiyoではないことは明らかなの、Piyoのメソッドは呼べない
// 2.0未満では、ここではコンパイルエラーにならない
// unknownObj.exec()
}
}
正直ややこしすぎてよくわからんですが…
インクリメントやデクリメントしたときに型が判明したときにSmart castされる感じです。
参考
(Kotlinの whatsnew-eap.html
のページは将来的に変わっちゃうので、GitHubのハッシュを指定しています。)