Yabu.log

ITなどの雑記

Java読書会BOF「Kotlinイン・アクション」を読む会 第5回に参加

7,8,9章あたりを読みました。 ジェネリクス、ラムダなどJavaだとちょっと不便なところをKotlinがどう解決しているか?というのが見所だと思います。

コード

自分が朗読するパート以外は今回からコードを動かしながら拝聴しました。 https://github.com/Kotlin/kotlin-in-action

分割宣言

複数の戻り値を持つ関数

分割代入を利用する

class Sums(val sum: Int, val count: Int){
    //componentNで
    operator fun component1() = sum
    operator fun component2() =count
}

data class Sums2(val sum: Int, val count:Int){
}


/**
 * 合計と要素数を返す関数
 */
fun multiRet(arg:List<Int>):Sums{
    return Sums(arg.sum(),arg.count());
    //return Sums2(arg.sum(),arg.count());

}


fun main(args: Array<String>) {
    val numbers = listOf(1,2,3,4)
    val (a,b)=multiRet(numbers)
    //上記の一文は以下のように解釈される
    //val a = multiRet(numbers).component1()
    //val b = multiRet(numbers).component2()


    println(a)
    println(b)
}
public final String component1()
{
    return name;
}

public final int component2()
{
    return age;
}

分割宣言とループとの組み合わせ

コレクションの各クラスには独自にcomponentN関数が実装されており、その恩恵を受けるような書き方ができる。

  • Mapのentryにはcomponent1,2がそれぞれkey,valueに対応付けられている。
fun printEntries(map:Map<String,String>){

    
    for((key,value) in map){
        println("$key -> $value")
    }
    /*
    //mapのentryのにはcomponentNがkey,valueに実装されており、
    //上記のfor文の生身はこんな感じに解釈される
    for (entry in map.entries){
        val key = entry.key
        val value = entry.value
        println("$key -> $value")
    }
    
     */
}
fun main(args: Array<String>) {
    val map = mapOf("Oracle" to "Java", "JB" to "Kotlin")
    printEntries(map)
    /*
    結果
    Oracle -> Java
    JB -> Kotlin
     */
}

移譲の例

ORマッパーのようなものを移譲を利用して簡単に作成できる

  • オブザーバーパターンを活用
    • 監視した値が変わったら更新
  • これはKotlinでなくてもORマッパーはObserverパターンを利用しているのかな?
    • 著名なOSSのORM(Hibernateしか知らんが)のソースをみに行ったがよくわからなかった

withIndexを使ったfor文

分割代入を利用して拡張for文内でindexを使えるようにする

Javaだとfor-each文のなかでindexを扱えないので、 for外のスコープに定義した変数をループの中でインクリメントしてごまかしたりしますが、 Kotlinだと次のように書くと拡張for文内でもindexを使えます

val numbers = listOf(21,19,18)
    for((index,value) in numbers.withIndex()){
        println("$index : $value")
    }

出力

0 : 21
1 : 19
2 : 18

withIndex()がDataクラスであるIndexedValue(index: Int, value: T)イテレータを返しているようです。こちらはデータクラスなので分割代入に対応しています。

https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/with-index.html

https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/-indexed-value/index.html

デザインパターン関数型プログラミング

276pより

  • ストラテジーパターンは高階関数で実現できる。
  • 振る舞い(ストラテジー)を外部からラムダとして渡す

design patternでstack overflowを検索すると 最上位に関数型プログラミングデザインパターンを置き換える?と行った内容の議論が出てくることを思い出しました。

https://stackoverflow.com/questions/327955/does-functional-programming-replace-gof-design-patterns

インライン展開

p279あたり

https://ja.wikipedia.org/wiki/インライン展開

  • intelliJはよく、インライン化しろ!と命令してくるらしい。(経験者談)
  • インラインはテンプレートではなくマクロっぽい。

withLock関数でtry-finally節を省略できる

import java.util.concurrent.locks.Lockをかなりシンプルに扱う機能のです

Lock l = ...;
l.lock();
try {
  // access the resource protected by this lock
} finally {
  l.unlock();
}

これ相当のプログラムがKotlinでは以下のようにかけます

val l:lock = ...
l.withLock{
  //ロックで保護したリソースにアクセス
}

かなり強力ではないでしょうか?

javaのtry-with-resources文

  • Kotlinにない。 = 代替としてuse関数を使う
import java.io.BufferedReader
import java.io.FileReader
import java.io.File

fun readFirstLineFromFile(path: String): String {
    BufferedReader(FileReader(path)).use { br ->
        return br.readLine()
    }
}

Closableインターフケースが実装されている必要があります。try-with-resourcesのAutoCloseable相当なのでしょうか?

拡張関数はどこに?

探すの大変そう。

  • C#でも同じ苦労がある
  • 知ってたら使えるが、知らなかったら永久に存在がわからない。
  • ラッパークラスを作るより、拡張関数の方が良いという意見もある。

型パラメータに対する複数の制約の指定

<>の中で一つだけ上限境界(upper bound)*1を指定できます。 複数の型パラメータをもたせたい場合はwhereを使います JavaでおんなじことやろうとするinstanceOfで型検査する必要があります(多分この方法しかないと思う((単数だとsuer ,extendsなどでできた気がするが、複数はできなかったと思う)))。

fun <T> ensureTrailingPeriod(seq: T)
        where T : CharSequence, T : Appendable {
    if (!seq.endsWith('.')) {
        seq.append('.')
    }
}

fun main(args: Array<String>) {
    val helloWorld = StringBuilder("Hello World")
    ensureTrailingPeriod(helloWorld)
    println(helloWorld)
}

実行時に消去されない方引数(reified)

p308ページあたり

  • Javaでは実行時に型引数を利用して型検査はできない。
  • Kotlinではreified
    • Javaからは呼び出せなくなる
  • 常にインライン展開される必要がある

9.3.2 クラス、型、サブタイプ

これはEffective Javaにも出てくる話ですね。ほったらかしにしている3rd(英語版)があるので、後日復習して書こうと思います。

  • 共変
  • 不変
  • 反変

Kotlinイン・アクション

Kotlinイン・アクション

*1:上限境界:そのクラスか、そのサブクラスのみに制限できる

*2:実行時に型情報は消える