カテゴリー: kotlin

型判定とキャスト : kotlin

型判定とキャスト

is演算子と!演算子による型判定

※これis演算子と否定の!演算子の組み合わせだから、こういう表現の方が正しいような気がするけれども、どうだろうか(´・ω・`)?

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

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

if (obj !is String) { // !(obj is String)と同じです。
  print("Not a String")
}
else {
  print(obj.length)
}

スマートキャスト

Kotlinでは多くの場合、キャスト演算子を明示的に使用する必要はありません。コンパイラは不変の値に対するis演算子の使用を探知し、必要に応じて(安全な)キャストを自動的に挿入するからです:

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

コンパイラは、否定判定の結果がReturnになる場合にキャストが安全になること(キャストが発生しないこと?)を理解するほどに賢いです: ←※いろんな翻訳サイトを使ってもいまいち翻訳しきれなかったのでちょっと超訳(´・ω・`)シドニィ

if (x !is String) return
  print(x.length) // xは自動的にStringにキャストされます。

また&&および||演算子の右側でも:

  // `||`の右側でxは自動的に文字列にキャストされます。…(1)(2)
  if (x !is String || x.length == 0) return

  // `&&`の右側でxは自動的に文字列にキャストされます。…(2)
  // 同上
  if (x is String && x.length > 0)
      print(x.length) // xは自動的にStringにキャストされます。

(1)原文に即して翻訳したけれども、コンパイラはis判定でtrue(または!is判定でfalse)で、後の行でその変数を使用する場合にのみ即時キャストするのか、使用直前でキャストするのかでここの文言が変わって来るような気がする。そういう意味合いにおいてはいつキャストされるかということを明示した方がいいのではなかろうか?
(2)stringじゃなくてStringだな。ここの二箇所だけstringになってる。どうでもいいけどw

このようなスマートキャストは、when式とwhileループでも機能します。:

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

コンパイラが判定時と実際の使用時の間で変数が不変であることを保証できない場合、スマートキャストは機能しないことに注意してください。より具体的には、スマートキャストは次のルールに従って適用されます。:

valローカル変数 - 常時;
valプロパティ - プロパティがprivateまたはinternalである場合、またはプロパティが宣言されているのと同じモジュールで判定が実行される場合。スマートキャストは、openのついた継承可能なプロパティまたはカスタムゲッターがあるプロパティには適用されません。
varローカル変数 - 変数が判定時と実際の使用時の間で変更されておらず、それを変更するラムダに捕捉されていない場合。
※Kotlinのラムダ式はラムダ式の中から、外側の変数の値を変更することができ、これを捕捉できると表現する模様。まあ詰まるところ変更されない限りということだろうか。https://maku77.github.io/kotlin/basic/lambda.html
varプロパティ- 不可。(このvarプロパティは他のコードによっていつでも変更されうる為。)


"アンセーフ(安全ではない)"キャスト演算子

通常、キャストが不可能な場合、キャスト演算子は例外をスローします。したがって、私たちはそれをアンセーフ(安全ではない)と呼びます。 Kotlinでの安全ではないキャストは、中置演算子 asによって行われます("演算子の優先順位"を参照)。:

val x: String = y as String

この型はnull許容ではないため、nullをStringにキャストできないことに注意してください。つまり、yがnullの場合、上記のコードは例外をスローします。 Javaキャストのセマンティクスに一致させるには、キャストの右側にnull許容型が必要です。
※キャストの右側だけじゃなくて左側の宣言の型にも必要なんじゃ? それとも型の右側という意味?

val x: String? = y as String?

"セーフ(安全な)"(null許容)キャスト演算子

例外がスローされるのを避けるために、失敗時にnullを返すセーフキャスト演算子as?を使用できます。

val x: String? = y as? String

as?の右側が非null型の文字列であることに関わらず、キャストの結果はnull許容型です。

\コットリ~ン/

ガチで勉強する気も何かを作る予定もないものの、今年は余りにも何もしなかったので、現実逃避的にkotlinを触り始めた(´・ω・`)

まあ中古ゲームや漫画の消化の傍らにkotlin koansをやったり、その延長線上でkotlinの解説ページを眺めたり翻訳したりしてるだけだけど。
(実際のところ、翻訳はほぼほぼごっぐる先生の翻訳である(ノ∀`))

記憶力が低いのか新しいことを勉強すると、前にやっていたことのほとんどを忘れてしまうたちなので、適当にエントリ化して記録しておこう("・ω・゙)トシダシネ


IntelliJ IDEA

取り敢えずIDEはIntelliJ IDEAに。
Android Studioでも触れるようだけれども、まあKotlin Nativeとかも試してみたいので、IntelliJ IDEAの方にしておいた。

スニペットコードを試す分には何の問題もなかったが、コンソールアプリケーションを実行しようとした時に以下のようなエラーが発生。

java.lang.NoClassDefFoundError: org/jetbrains/kotlin/cli/common/PropertiesKt at org.jetbrains.kotlin.gradle.plugin.KotlinBasePluginWrapper.apply(KotlinPluginWrapper.kt:82)

なんかようわからんけど、Project JDKのバージョンの問題だかどうとか。
最初はProject JDKを15にしていて駄目だったから、取り敢えずcorretto 1.8だかの古いのを落として来て設定したんだったかな…?

最初は[Build, Execution, Deployment]-[Build Toos]-[Compiler]-[Java Compiler]の[Project bytecode version]を変更すればいいのかと思ったがそうではなかったような…あんまり細かいことは覚えていない(ノ∀`)

何はともあれ、[Build, Execution, Deployment]-[Build Toos]-[Build Tools]-[Gradle]の[Gradle JVM]を落としてきた古いやつに変えたら上手くビルド出来るようになったんだったか…確か……

この辺のJDKとかのエラーってもっとこうインテリジェントにというか、IDE側で検知して解決策を提示してくれるように出来ないのであろうか(´・ω・`)?


Kotlin Koans

問題集みたいなプロジェクトパックのKotlin KoansというのがあるのでEduToolプラグインを入れてやり始めてみた。

koanってなんだろって思ったら、元ネタは禅問答の公案らしい。

公案(こうあん)とは、禅宗で修行僧が参究する課題である。日本では昔から1千7百則とも言われ、法身、機関、言詮、難透などに大別されるが、その他に様々な課題がある。内容はいわゆる禅問答であって、にわかに要領を得ず、解答があるかすら不明なものである。有名な公案として「隻手の声」、「狗子仏性」、「祖師西来意」などがある。

 例: 両手を叩くと音がする。では片手の音とはなんだろう。(隻手の声)
公案


ちょこっとした問題文とコードが書いてあって、それを修正してチェックボタンを押して正誤判定するみたいな感じ……

…なんだけど、これって誰を対象にしてるんだがよくわからないw
ごっぐるのAndroid Developersもそうなんだけど、どのくらいのレベルの人に何を教えようとしているのかがよくわからないw

元々のkotlinの基礎知識がないと各問題にリンクされている参考ページを読んだだけでは、すぐに何をしていいかわからなくなると思う。

少なからず、kotlinのサイトのGetting Startedを上から順にやっていって、Koansのところに辿り着いた段階の俺氏にはさっぽろわっかない状態だった(´・ω・`)

結局色々なサイトや日本語リファレンスをそれなりに読みながらじゃないと理解は進まないと思った。

日本語リファレンスも途中までしか訳されていなくて、しかもなんか新しいバージョンの英語リファレンスとかもあったりするので、プログラミング界の永遠のnewbeeである俺氏は混乱せざるを得ない状況である(´・ω・`)


「Named arguments」の回答って、"Make the function joinOptions() return the list in a JSON format (e.g., "[a, b, c]") by specifying only two arguments."なので2つの引数しか許さないのかなと思ったけれども、

fun joinOptions(options: Collection<String>) =
        options.joinToString(
                ", ",
                "[",
                "]"
                )

なんて形で3つの引数を渡してもパスしちゃうのは何故なんだぜ(´・ω・`)?
問題文として条件は出してるものの、システム的なチェックはしてないのかな?

単純にdiffとかしてるんじゃないのか…?
failing unit testを使ってるとか書いてあるな…


全然関係ないけれども、IntelliJ IDEAはコード表示部分以外のフォントサイズを簡単に変更出来るので、老眼等で小さい字が読みにくくなり、かといって馬鹿でかいディスプレイを入手できない俺氏にとっては嬉しいユーザービリティである(・∀・)


一年前くらいにもちょっと手を出してすぐ放置してしまったので、今回もそうなる可能性は高い(・∀・)

つーかこんなことをしていないで、Unity+C#の続きをやったり、真面目に仕事を探さないといかんな(ノ∀`)