PHP/Laravel => Kotlin/Spring Boot

差分だけ書く
変数はデフォルトでNot Nullable
// これはOK
String? = String
// これはNG まあ想像したらわかる
String = String?
変数宣言
valは変更不可、つまりValueObject
varは変更可、つまりEntity
あれ、定数はどうなるのかな
なるほど、意見分かれてるのかな constつけれる時はつけた方が良いのね。
コレクションを扱うときもforで良いんだ
for (value in list)
forEachもあるけどあんまり速くないみたい
インスタンス生成にnewは不要とな
val class = Human()
コンストラクタの定義はクラス名の後ろ
class Human(val name: String) {
fun greet() {
println("Hello There.")
}
}
とりあえず今日はここまで
次はp56から

継承させたいクラスや関数には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から

関数型の定義
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)
}
振り返りしよ

スコープ関数
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 が出てきたら要注意だなー
おわ

やっていき
デリゲート
処理の委譲をシンプルに記述する機能らしい
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も配列に関する処理は多かったので、まあまあ読み飛ばせそう
コルーチンで非同期処理
中断可能でノンブロッキングな非同期処理
中断可能... 処理を一時停止(中断)し、後で再開することができる。これにより、複数のタスクを簡単に並列化・並行化できる
ノンブロッキグ...コルーチンが一時停止している間、他のコルーチンやタスクがそのスレッドを使用して実行を続行できる
コルーチンスコープの中でコルーチンビルダーを使用して実装する必要がある
コルーチンスコープはコルーチンが実行される仮想領域のようなもの
超眠いから一旦終了

風邪でダウンしてるから復習だけする

やっていき
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 |
まあ大体一緒だから気にしないでいいや、実際に何かプロジェクト作りながら考えよ

やっていき
Spring Bootについて
アノテーションで色々できちゃうんだな GradleもComposerと同じく依存関係管理以外にもタスクランナーとしての本質があるみたい
テンプレートエンジンもあるけど、本格的な開発では使わないだろうな 南無
DIもある、いいね
DIしたオブジェクトはシングルトンになる
アプリケーション起動時に生されて、DIコンテナ内で使いまわされる
@Component
でDIの対象であること=シングルトン運用前提であることを宣言できる
そしてこれは実装クラスにかき、Interfaceを型としてDIすると実装クラスが自動的に生成され呼び出される
LaravelでいうServiceProviderを自動で登録してやってくれるような感じだね
1つのIFに対して複数の実装クラスが存在する場合は
@Component("エイリアス")
と実装クラスにかき、呼び出し元では
@Qualifier("エイリアス")
とすることでDIができる
終わり
次はORマッパー

やっていき
ORマッパー
MyBatis
まあ使い方はいいや パラシュートで覚えるし
あとKtor、Exposed、Kotestの説明があったけどこれらも覚えるからいいや

一旦クローズして、復習用に使おう

if typeチェックの避け方、いいね
service as? CustomerService ?: Throw IlligalArgumentsException("No CustomerService")
service.getCustomer()
手続きの象徴、ifが出てきたら気をつけよう。他の書き方があるはず

letもifを減らす助けになる
// ダメ
val order: Order? = findOrder()
if (order != null){
dun(order.customer)
}
findOrder()?.let { dun(it.customer) }
//or
findOrder?.customer?.let(::dun)