Open12

PHP/Laravel => Kotlin/Spring Boot

Taru-sourceTaru-source

差分だけ書く

変数はデフォルトでNot Nullable

// これはOK
String? = String

// これはNG まあ想像したらわかる
String = String?

変数宣言
valは変更不可、つまりValueObject
varは変更可、つまりEntity
あれ、定数はどうなるのかな
https://qiita.com/takusemba/items/f278e9e9e9217f0d6632
なるほど、意見分かれてるのかな constつけれる時はつけた方が良いのね。

コレクションを扱うときもforで良いんだ

for (value in list)

forEachもあるけどあんまり速くないみたい

インスタンス生成にnewは不要とな

val class = Human()

コンストラクタの定義はクラス名の後ろ

class Human(val name: String) {
    fun greet() {
        println("Hello There.")
   }
}

とりあえず今日はここまで
次はp56から

Taru-sourceTaru-source

継承させたいクラスや関数にはopenをつける
デフォルトでfinalがついてる感じ

継承はextendsじゃなくて: クラス名 これはIFも同じらしい

sealed class
他のクラスから継承できなくなる
同一ファイル内では継承できる

コレクションは三つある
ListとMapとSet

List
デフォルトでイミュータブル 変更可能にしたい場合はmutableListOf()で作る ほー

Map
key valueのコレクション
これもデフォルトでイミュータブル

Set

しかしSetにはListには無い大きな特徴が2つあります。その2つとは、
値が重複しない
インデックス番号で管理されない
https://pouhon.net/kotlin-set/1422/

なるほど

重複する要素を許容しないデータ構造が必要な場合
集合演算(和集合、積集合、差集合など)を行う場合
要素の存在チェックが高速であることが重要な場合(例:HashSet)

この場合はSetでいいね

制御構文を式として評価できるの便利そう

val text = if ( num % 2 == 1 ) {
   "奇数"
} else {
   "偶数"
}

プロパティの定義
getter、setterの儀式は不要
じゃあ不変のvalは?というとgetterのみらしい

lateinit var name: Stringで遅延初期化ができる、つまり後から値をsetするので当然varが必要になる

拡張プロパティと云ふもの

内部的に生成されるget()やset()を、プロパティごとに拡張することができる

class User {
  latinit var name: String
  val isValidName: Boolean
     get() = name != ""
}

val user = User()
user.name = "Taru-source"
println(user.isValidName). // true

データクラス
equals、hasCode、toStringとかのボイラープレート(テンプレ)を内包している
名前の通り、データを格納する場合に何かと役立つクラス

今日はここまで、次回は関数型と高階関数、タイプエイリアス p93から

Taru-sourceTaru-source

関数型の定義

val calc: (Int, Int) -> Int = { num1: Int, num2: Int -> num1 + num2 }

(引数 ...) -> 戻り値の型、という構文らしい

calに代入している値が{}内のものになる 関数を値として記述するものを関数リテラルというらしい
そして、↑の書き方はラムダ式

型推論が効くので、値部分の型は省略できるとのこと

引数が一つの場合は引数名も省略できる

val double: (Int) -> Int = { it * 2 }

その場合はitという名前で扱う

ラムダ式以外にも無名関数を使用することもできるけど、こっちは戻り値の型を明示的に記述する必要がある場合に使う感じとのこと まあわかりやすいしラムダ式でいいね

高階関数
関数型のオブジェクトを引数に受け取る関数のこと

fun printCalcResult(num1: Int, num2: Int, calc: (Int, Int) -> Int) {
    val result = calc(num, num2)
    println(result)
}

実行はこう

printCalcResult(10, 20, { num1, num2 -> num1 + num2 })
printCalcResult(10, 20, { num1, num2 -> num1 * num2 })

// Output
30
200

関数リテラルをそのまま渡して結果を出力することができる
同じ関数でも、引数に渡す関数リテラルで一部の処理を変えることができる

ワンライナーでは可読性が低いので、推奨される書き方はこれ

printCalcResult(10, 20) { num1, num2 -> 
    num1 * num2
}

リテラルを()の外に出すことが可能

タイプエイリアス
関数型にエイリアスをつけること
(Int, Int) -> Int ←を使いまわせる

typealias Calc = (Int, Int) -> Int

これを使うとこう

fun printCalcResult(num1: Int, num2: Int, calc: Calc) {
    var result = calc(num1, num2)
    println(result)
}

振り返りしよ

Taru-sourceTaru-source

スコープ関数
with

val list = mutableListOf<Int>()
for (i in 1..10) {
    if (i % 2 == 1) list.add(i)
}

val oddNumers = list.joinToString(separetor = " ")

// 1 3 5 7 9

これをwithで書き換えると

val oddNumbers = with(mutableListOf<Int>()) {
    for (i in 1..10) {
        if (i % 2 == 1) this.add(i)
    }
    this.joinToString(separetor = "")
}

// 1 3 5 7 9

第一引数にレシーバとしてオブジェクトを、第二引数に任意の型を返す関数を渡せる
レシーバに対してはthisでアクセスできる そして省略できる

val oddNumbers = with(mutableListOf<Int>()) {
    for (i in 1..10) {
        if (i % 2 == 1) add(i)
    }
    joinToString(separetor = "")
}

// 1 3 5 7 9

run
withと同じような感じだけど、nullableなオブジェクトに対して使える

data class User(var name: String)
fun getUserString(user: User?, newName: String): String? {
    return user?.run {
        name = newName
        toString()
    }
}

let
with、runとは違ってNullableなレシーバオブジェクトに対してthisではなくて名前をつけることができる
ただ、そのまま使うと冗長な書き方になるので、Nullableなオブジェクトに対して処理を実行するパターンでよく使われる

data class User(val name: String)

fun createUser(name: String?): User? {
    return name?.let { n => User(n) }
}

↑引数のnameがnullでなかった場合にUserインスタンスを返す nameがnullだったらnullを返す
レシーバは暗黙の省略でitにすることも可能
return name?.let { User(it) }
こんな感じ

意外と差分多くて書くのだるいからとりあえず一通り読み上げる

apply...dataクラスのようなプロパティを持ったオブジェクトに変更を加えて返却する時に役立つ しかし、ローカル変数との名前衝突を避けるために必ずthisを使うか、alsoを使う場合が多い
also...applyとほぼ一緒 しかし、letみたいにthis以外の名前 or itで省略できるので、ローカルとの名前衝突が起こらない

演算子オーバーロード
クラスに対する演算子の処理を実装できる ちょっとわかりにくいから書く

data class Num(val value: Int) {
    operator fun plus(num: Num) Num {
        return Num(value + num.value)
    }
}
// ↑+演算子のオーバーロード

val num = Num(5) + Num(1)
// Num(value=6)

operator fun が出てきたら要注意だなー

おわ

Taru-sourceTaru-source

やっていき
デリゲート
処理の委譲をシンプルに記述する機能らしい
by で委譲先を指定する

委譲プロパティ プロパティの実装を外部へ切り出して委譲する機能
これはちゃんと書いてみよう

// メッセージを出力する処理を持った委譲先のクラス
class DelegateWithMessage<T> {
    private var value: T? = null
    // 演算子オーバーロード
    // 委譲先にもgetValue、setValueにoperatorをつけて定義する必要がある
    operator fun getValue(thisREf: Any?, property: KProperty<*>): T {
        println("${property.name}を取得します")
        return value!!
    }

    operator fun setValue(thisRef: Any?, property: KProperty<*>, value: T) {
        println("${property.name}を更新します")
        this.value = value
    }
}

KProperty<*>が委譲元のプロパティ情報を持つ
thisRefには委譲元のオブジェクトの参照が入る

委譲するとこう

class DelegatePerson {
    var name: String by DelegateWithMessage()
    var address: String by DelegateWithMessage()
}

充実したコレクションライブラリ
phpも配列に関する処理は多かったので、まあまあ読み飛ばせそう

コルーチンで非同期処理
中断可能でノンブロッキングな非同期処理
中断可能... 処理を一時停止(中断)し、後で再開することができる。これにより、複数のタスクを簡単に並列化・並行化できる
ノンブロッキグ...コルーチンが一時停止している間、他のコルーチンやタスクがそのスレッドを使用して実行を続行できる

コルーチンスコープの中でコルーチンビルダーを使用して実装する必要がある
コルーチンスコープはコルーチンが実行される仮想領域のようなもの

超眠いから一旦終了

Taru-sourceTaru-source

やっていき

javaとkotlinは相互互換性(相互呼び出しができる)があるらしいので、部分的にリファクタリングするとかjavaだけのライブラリ使いたいとかの時に覚えておこう

companion object
クラス内にstaticな変数や関数を定義するときに使用する

ここからSpring bootでのアプリケーション開発へ これもLaravelとの差分を書き出す感じにしよ
chatGPTに聞いた主要ライブラリの比較

機能カテゴリ Spring Boot機能 Laravel機能
フレームワークの基本 Spring Framework Laravel Framework
テンプレートエンジン Thymeleaf, FreeMarker, Velocity Blade, Twig
データベース接続 Spring Data JPA, JDBC Eloquent ORM, Query Builder
ルーティング Spring MVC Laravel Routing
セキュリティ Spring Security Laravel Authentication, Gates, Policies
メール送信 Spring Mail Laravel Mail
キャッシュ Spring Cache Laravel Cache
バリデーション Spring Validation Laravel Validation
セッション管理 Spring Session Laravel Session
ファイルアップロード Spring MVC File Upload Laravel File Storage
RESTful API Spring REST Laravel API Resources
テスト Spring Test Laravel Dusk, PHPUnit
イベントハンドリング Spring Events Laravel Events, Listeners
ジョブキュー Spring Batch, Spring Task Laravel Queue, Jobs
タスクスケジューリング Spring Task Scheduler Laravel Task Scheduler
パッケージ管理 Maven, Gradle Composer

まあ大体一緒だから気にしないでいいや、実際に何かプロジェクト作りながら考えよ

Taru-sourceTaru-source

やっていき
Spring Bootについて
アノテーションで色々できちゃうんだな GradleもComposerと同じく依存関係管理以外にもタスクランナーとしての本質があるみたい
テンプレートエンジンもあるけど、本格的な開発では使わないだろうな 南無

DIもある、いいね
DIしたオブジェクトはシングルトンになる
アプリケーション起動時に生されて、DIコンテナ内で使いまわされる

@Component

でDIの対象であること=シングルトン運用前提であることを宣言できる
そしてこれは実装クラスにかき、Interfaceを型としてDIすると実装クラスが自動的に生成され呼び出される
LaravelでいうServiceProviderを自動で登録してやってくれるような感じだね

1つのIFに対して複数の実装クラスが存在する場合は

@Component("エイリアス")

と実装クラスにかき、呼び出し元では

@Qualifier("エイリアス")

とすることでDIができる

終わり
次はORマッパー

Taru-sourceTaru-source

やっていき
ORマッパー
MyBatis
まあ使い方はいいや パラシュートで覚えるし

あとKtor、Exposed、Kotestの説明があったけどこれらも覚えるからいいや

Taru-sourceTaru-source

if typeチェックの避け方、いいね

service as? CustomerService ?: Throw IlligalArgumentsException("No CustomerService")
service.getCustomer()

手続きの象徴、ifが出てきたら気をつけよう。他の書き方があるはず

Taru-sourceTaru-source

letもifを減らす助けになる

// ダメ
val order: Order? = findOrder()
if (order != null){
    dun(order.customer)
}
findOrder()?.let { dun(it.customer) }
//or
findOrder?.customer?.let(::dun)