KotlinConf 2018でも紹介しましたがAtomKotlinでのkotlin勉強はなかなかいい感じでした。
https://www.atomickotlin.com/atomickotlin/
IntelliJでコードを実行しながら確認できるのでvideoを見るだけには得られないものが多い。
自分の中で曖昧だったポイント整理:
varargでの可変数パラメタ使い方
fun sum(vararg numbers: Int): Int {
var total = 0
for (n in numbers)
total += n
return total
}
sum(13, 27, 44)
val array = intArrayOf(4, 5) //java的にはint[]配列
sum(1, 2, 3, *array, 6) //*でarrayの値リストを展開
val list = listOf(9, 10, 11) //list型
sum(*list.toIntArray()) //list型をint[]型に変換する必要がある
sum() //varargはパラメタが>=0なので0個でも問題ない。
lambdaの二つの書き方
難しく見えますが実際はすでに知っている関数と拡張関数を分かっていれば理解できます。
二つのlambda定義方法
1.関数パラメタをプリミティブ型ではなく関数で定義をする
2.関数のパラメタをプリミティブ型ではなく拡張関数で定義する
fun buildString1(
builderAction:
(StringBuilder) -> Unit // [1] fun builderAction(sb: StringBuilder)と関数を定義した。
): String {
val sb = StringBuilder()
builderAction(sb) // [2] 関数の呼び方と同じである
return sb.toString()
}
fun buildString2(
builderAction:
StringBuilder.() -> Unit // [3] StringBuilderの拡張関数の定義に似ている
): String {
val sb = StringBuilder()
sb.builderAction() // [4] 内部的にStringBuilderの拡張関数(名前builderAction)なのでそのまま使える
return sb.toString()
}
fun main(args: Array<String>) {
//普通
buildString1 {
it.append("Regular lambda")
} eq "Regular lambda"
//receiver経由
buildString2 {
append("Lambda with receiver") //StringBuilderの拡張関数の定義と同じ
} eq "Lambda with receiver"
}
Scope関数:run/let/with/apply/also
これはまとめると下記のようだ。
nullableチェック可能なのはwith以外全部ok
with以外は全部拡張関数で定義されるているため、例えばrunの定義のT.run:
public inline fun T.run(block: T.() -> R): R
メンバー変数の扱い安いものはapply,with,run
理由は{}の中でthisで扱えるため省略できる
戻る値をカスタムする場合はlet,run,with
理由は戻りがthisではなく{}のblockの実行結果のため
実際のコーディングではオブジェクトのメンバーを設定した後に直接その自身のthisを返すapply, alsoが便利です。特にapplyは結構シンプルに書けます。
検証コードは下記のようです。
class Frame(
val id: String,
var x: Int = 0,
var y: Int = 0,
var width: Int = 200,
var height: Int = 100
) {
override fun toString(): String {
return "Frame(id='$id', x=$x, y=$y, width=$width, height=$height)"
}
}
fun main(args: Array<String>) {
val frameNullable: Frame? = Frame("frame")
val frame: Frame = Frame("frame")
// let
// 1.customの名前づけられる
// 2.戻りは最後の行
// 3.nullチェックにも使える
val letResult = frameNullable?.let { customName ->
customName.x = 33
"let success"
}
// run
// 1.thisなのでcustomの名前づけられない、itを付けなくでも簡単にobjectを操作できる
// 2.戻りは最後の行
// 3.nullチェックにも使える
val runResult = frameNullable?.run {
x = 33
"run success"
}
// with
// 1.thisなのでcustomの名前づけられない、itを付けなくでも簡単にobjectを操作できる
// 2.戻りは最後の行
val withResult = with(frame) {
x = 33
"with success"
}
// apply
// 1.thisなのでcustomの名前づけられない、itを付けなくでも簡単にobjectを操作できる
// 2.戻りは自分自身(this)なので指定する必要がない
// 3.nullチェックにも使える
val applyResult = frameNullable?.apply {
x = 33
//戻りはframeNullable自身
}
// also
// 1.customの名前づけられる
// 2.戻りは自分自身(this)なので指定する必要がない
// 3.nullチェックにも使える
val alsoResult = frameNullable?.also { customAlso ->
customAlso.x = 88
//戻りはframeNullable自身
}
println("$letResult")
println("$runResult")
println("$withResult")
println("$applyResult")
println("$alsoResult")
// 結果
// let success
// run success
// with success
// Frame(id='frame', x=33, y=0, width=200, height=100)
// Frame(id='frame', x=88, y=0, width=200, height=100)
}
listのsequence化によって処理が高速化
巨大なファイルもしくはデータの処理に便利です!
全ての条件をチェックするので事前に全部のデータをメモリに貯めるのを回避できる。必要な分だけメモリに読み込みができます。
この方の説明がいいですね。
https://speakerdeck.com/ryotamurohoshi/sequencefalsehua-kotlin-dot-sequences-dot-sequence
val r2 = list.asSequence()
.filter(Int::isEven)
.map(Int::square)
.any(Int::lessThanTen)
takeIfとgenerateSequenceの例
fun main(args: Array<String>) {
val lines = generateSequence {
readLine()?.takeIf { it != "XXX" }
}
println(lines.toList())
}