🚀

【1日1zenn - day10】kotlinの超ざっくりキャッチアップ

2024/12/24に公開

BEのチケットをもらい、既存仕様等のキャッチアップを進めていたのですが、めちゃくちゃ時間がかかりました。
かかった要因として、ざっくり大事そうなところを見ようとしていたので見落としていた処理があったことが大きく、
それが発生したのは『多分ここが大事』の勘所を掴めていなかったのと、1行ずつ読むには自分の知識が甘く認知負荷が高すぎたことがあったように思えます。

自分のチームはkotlinとSpring Bootでバックエンドを書いているのですが、研修で習ったJavaの机上知識でなんとなく直感的に理解できる部分もあるからザザッと読んじゃうみたいなことをしてしまっており、
小さいAPI修正くらいなら既存コードを真似しながら書けちゃったりするものの、完全新規のAPI新設は書ける気がしていません。

とはいえ今日は疲れたので、そういうAPI新設はゴールにせず、まずKotlinの雛形キャッチアップをメインにしようと思います。
明日はこれの続きで、何かしらAPIをゼロから作ろうかなと思いつつ、体力次第。

ざっくりキャッチアップ

一旦kotlinのドキュメントを読む

基本的な構文

ざっくり感想メモ
https://kotlinlang.org/docs/basic-syntax.html#variables

  • valは値を再代入できない。varは再代入できる
    • Reactでいうconstがvalで、letがvar。
      • value(値)がvalで、variable(変数)がvarらしい。覚えられそう。
  • Kotlin は型推論をサポートしており、宣言された変数のデータ型を自動的に識別する
    • けど実務では定義してることが多そう
  • 変数は初期化した後にのみ使用できます。宣言時に変数を初期化するか、最初に変数を宣言して後で初期化することができます。後者の場合、データ型を指定する必要があります。
    • kotlinのconstructorとか継承、Reactより大事な感じするからちゃんと見ようというメモ
    • まあReactのlet同様、実務のコードではvarはあまり見かけなくて、多分今何が入ってるか分かりにくくて可読性が低いからかな
  • クラス宣言にリストされているパラメータを持つデフォルトのコンストラクターは自動的に使用可能になります。
class Rectangle(val height: Double, val length: Double) {
    val perimeter = (height + length) * 2 
}
fun main() {
    val rectangle = Rectangle(5.0, 2.0)
    println("The perimeter is ${rectangle.perimeter}")
}
  • mainで宣言しなくても使えるということ?ここは掘り下げる

  • クラス間の継承はコロン ( :) で宣言されます。クラスはfinalデフォルトで継承可能になっています。クラスを継承可能にするには、次のようにマークしますopen。

open class Shape

class Rectangle(val height: Double, val length: Double): Shape() {
    val perimeter = (height + length) * 2
}
  • ここはShapeに何か追加して使いたいときにはこう書きましょうということで、このShapeが引数を取るときにパッと見Reactの感覚だとよくわからん書き方になったりする気がする。

  • forとかmapとかは他と変わらない

  • isで型をチェックできるらしい

    • if(!obj is String)とか

ここまで見て、俺に必要なのはSpringな気がしてきた。
UrlComponentsBuilderみたいなのとか、そもそもURIのコンポーネントって何やねんという気持ちになるが、全部調べてたらキリがないからフィーリングで察してた。
まあ今日は焦らずKotlinやろう。

イディオム

https://kotlinlang.org/docs/idioms.html#default-values-for-function-parameters

  • data classはkotlinの強み的な概念だった気がする
  • val positives = list.filter { it > 0 }みたいにfilterを簡単にかけたりもするらしい
  • if ("jane@example.com" !in emailsList) { ... }で含まれるか確認したり
  • 文字列もprintln("Hello, $name")で結合できる。Javaみたいに「+」とか不要

クラス

https://kotlinlang.org/docs/classes.html

  • そもそもコンストラクタについて曖昧なまま歩いてる気がしたので改めて調べる
    • https://wa3.i-3-i.info/word13646.html
      • そのクラスを呼び出したときに実行される関数
        • 値の宣言とか代入だけじゃなくて、戻り値がない関数なら何でもできる
    • 以下はコンストラクタで書くべき処理な理解
      • インスタンスを呼び出したときにマストで実行すべき
      • テストが不要
      • 戻り値がない
      • エラーを返す可能性がない
      • 処理に時間がかからない
      • 使わないユースケースがない
    • 要は上記のようなマストな処理は忘れないようにコンストラクタでやっちゃおうという。
  • kotlinだとコンストラクタ内で、デフォルトの値を決めたりprivateみたいなアクセス修飾子をつけたりできる
    • アクセス修飾子をつけたい場合はclass Customer public @Inject constructor(name: String)みたいにconstructorというワードを明示的に書く必要があるけど、そうじゃないなら省略していい
  • セカンダリコンストラクターはドキュメント読んでもむずかったので調べる
    • https://kotlin-study.com/secondary-constructor/
      • 複数のコンストラクタが必要になるときに、必要に応じて使う
        • いつ必要になるんだ??
          • 引数の数に応じて処理を変えたいとき
          • プライマリコンストラクタは全ての引数が必須になるので、省略可能にしたい引数がある場合や、引数に応じて初期化処理を変えたい場合に便利
      • constructorは必須でつける。thisはプライマリコンストラクタのこと
      • 逆にinitはつけず、{}の中にコードを書くだけでいいらしい
  val rinko = Rinko("コト", "リンコ")
}

class Rinko(val name: String, var age: Int) {
  // 下記がプライマリコンストラクタのイニシャライザ
  init {
    println("プライマリコンストラクタのイニシャライザを実行。")
    println("受け取ったパラメータは${name}、${age}歳です。")
  }
  // 下記がセカンダリコンストラクタ
  constructor (age: Int) : this("無名", age) {
    println("年齢のみを受け取ったセカンダリコンストラクタのイニシャライザを実行。")
  }
  
  // 下記はもう1つのセカンダリコンストラクタ
  constructor (last_name: String, first_name: String) : this("$last_name $first_name" , 10) {
    println("苗字と名前を受け取るセカンダリコンストラクタのイニシャライザを実行。")
  }
}
  • この処理の場合、「コト」と「リンコ」を引数にRinko()が呼ばれるので、StringとIntを受け取るプライマリコンストラクタは無視される
  • セカンダリコンストラクタの一つめもintなので無視
  • 最後のコンストラクタがStringを2つ受け取るので発動し、プライマリコンストラクタのthisに姓名を合体させたStringと10というintを渡すのでプライマリコンストラクタも実行される
    • これhttps://kotlinlang.org/docs/keyword-reference.html#soft-keywords を見ると、thisはプライマリコンストラクタに限らず、同じクラス内のコンストラクタを呼び出せそう
  • その結果、セカンダリコンストラクタ内の処理も、プライマリコンストラクタ内の処理も実行される

なるほど、ざっくりわかった気がする。

一旦おしまい

掘れば無限な感じはしつつ、実務の他のファイルとかを読んでみてわからないの調べたら基本Springについてだったので、springやろうかなと。
あとはこれ見る過程でいくつかの処理の流れを辿ったりしたので、シンプルに目が慣れた感じもする。知識とは別で、慣れみたいなのもありそうだね。

積み残し

  • springをざっくりキャッチアップする
  • その上でいくつかの処理を見てみて、キャッチアップ速度のボトルネックになりそうなところを調べる
    • springの解像度が上がることで、kotlinの不足も見えてきそう

ここ最近の感想

10日目なので継続判断も兼ねて振り返ろうかなと。

  • マーケと違って中長期で見た正確性などがより重視されプログラミングにおいて、自分は早く成果を出さねばと焦っていた
  • その結果AIに頼りまくったりして、実際初学者ではありえないほど早く実装できたりもしたが、ハルシネーションに気付けなかったことも要因の一つとなってバグを起こしたり、知識不足によるレビューバックの増加=他メンバーのリソース浪費を生んだりしていた
  • 筋トレと同じで、短期的な成果に焦るとむしろ諦めや品質低下につながると思ったので、筋トレ同様に「やったかどうか」という行動有無をKPIにし、筋トレのようなある程度の負荷がかかることを毎日やり続けようと考えて1日1zennという取り組みを始めた
  • その結果、絶対にアウトプットしなければいけないという目標達成のためにふとした疑問も放置しなくなり、むしろネタが尽きた時は拾いにいくようになった
  • まだ目に見えるコードの品質向上や速度向上といった定量的な成果は出ていないが、着実にいい方向へと向かっている気はする。
    • Reactの関数コンポーネントが再現しているライフサイクルやhooksの話とか、そもそもアロー関数と普通の呼び出しの違いとか、cssのrgba指定とか、超土台となる部分やいつかつまづきそうな細かい穴を埋めていけてて、実務でも分かりながら書けていると感じる部分が増えた感覚がある
    • 超基礎的な部分はいちいちAIに聞くより自分で書いた方が早い
  • 筋トレだと1ヶ月は最低限変化がないし、目に見えて変化が生まれるのは3ヶ月くらいだったりするので、3ヶ月後に爆変化が生まれていることを期待しつつ、焦らず続けていく
  • moreとして、定期的に記事を振り返って積み残しを拾おうと思う。放置されてる積み残しが結構ある
    • mockの方法とか、まだ抜け漏れあるしそれ踏まえて色んな処理のテストを書くとかやってない
    • けどまあネタ切れしたタイミングでやるとかで自然と実行されそうな気はする
    • 積み残しを毎回コピペで貯めておくとか明日からやろうかな。など。

Discussion