Kotlinのコーディングが捗る標準ライブラリ



[Kotlin Advent Calendar 2015](http://www.adventar.org/calendars/857)の19日目の記事です。
昨日は @magie-poohさんの[業務でKotlinを書いている僕がKotlinを書く際に個人的に注意していること](http://qiita.com/magie-pooh/items/b1179af28f5e0d50b62a)でした。

Kotlinは[標準ライブラリ](https://kotlinlang.org/api/latest/jvm/stdlib/index.html)として色々便利なAPIを提供してくれています。

しかし、今までのAndroidの開発ではCollectionやProperty系など一部しか使っていませんでした。そこで、もっとコーディングが捗るものがあるのでは、ということでざっと標準ライブラリを使ってみて便利（そう）だと感じたものをメモしておきます。
（CollectionやProperty系は使い方の色々なところで記事になっていると思いますので、今回は対象にはしていません。）

大まかな構成はkotlinソースコードのstdlibフォルダに合わせています。

* concurrent
* io
* text
* util

動作確認環境

* 1.0.0-beta-3595

※ベータ版のため今後のアップデートで変更があるかもしれません。

###  kotlin.concurrent

#### Thread.kt

* **thread(Boolean, ClassLoader?, String?, Int, () -> Unit): Thread**  
	ラムダ式でスレッド生成するトップレベル関数。

```kotlin
// デフォルトでは生成直後にスタート
thread {
    Thread.sleep(3000L)	
}

// 省略したデフォルト引数
thread(start = true, 
       isDaemon = true, 
       contextClassLoader = null, 
       name = null, 
       priority = -1) {
}
```

* **currentThread: Thread**  
	現在のスレッドトップレを取得するトップレベルプロパティ。

```kotlin
// メインスレッドかどうかの判定
currentThread == Looper.getMainLooper().getThread()
```

### kotlin.io

#### files.Utils.kt

* **File.copyTo(File, Boolean, Int): Long**  
	ファイルをコピーする拡張関数。

```kotlin
val src = File("text.txt")
val dest = File("dest/text.txt")
src.copyTo(dest, overwrite = true)
```

* **File.copyRecursively(File, (File, IOException) -> OnErrorAction): Boolean**  
	ディレクトリごとコピーするする拡張関数。

```kotlin
val src = File("src")
val dest = File("dest")
src.copyRecursively(dest) { file, error ->
    // コピーエラー
    OnErrorAction.SKIP // or TERMINATE
}
```

* **File.deleteRecursively(): Boolean**  
	ディレクトリごと削除する拡張関数。

```kotlin
val dir = File("to_delete")
val success = dir.deleteRecursively()
```

* **File.extension: String**  
	ファイルの拡張子を返す拡張プロパティ。

```kotlin
val textFile = File("text.txt").absoluteFile
println("extension: ${text.extension}")	// >extension: txt
```

* **File.nameWithoutExtension: String**  
	ファイル名のみ返す拡張プロパティ。

```kotlin
val text = File("text.txt").absoluteFile
println("filename: ${textFile.nameWithoutExtension}") // >filename: text
```

* **File.listFiles((File) -> Boolean)**  
	File#listFiles(FileFilter)の高階関数版の拡張関数。

```kotlin
val dir = File("contains_text")
dir.listFiles { it.isFile && it.extension.equals("txt") }
    ?.forEach { println("Hit: $it") }
```

#### file.FileReadWrite.kt

```text.txt
Kotlin1

Kotlin2
```

* **File.readLines(Charset)**
* **File.readLines(String)**  
	ファイル内容を文字列のリスト（行単位）で返す拡張関数。

```kotlin
val textFile = File("text.txt").absoluteFile
// 空行を除く
val lines = src.readLines().filter(String::isNotBlank)
				.toList() 
// >[Kotlin1, Kotlin2]
```
* **File.forEachLine(Charset, (String)->Unit)**
* **File.forEachLine(String, (String)->Unit)**  
	ファイル内容を行単位で取得する拡張関数

```kotlin
val textFile = File("text.txt").absoluteFile
var lines = ArrayList<String>()
src.forEachLine {
    if (it.isNotBlank()) {
        lines.add(it)
    }
}
// >[Kotlin1, Kotlin2]
```

* **File.writeText(String, Charset)**
* **File.writeText(String, String)**  
	ファイルにテキストを書き込む拡張関数。

```kotlin
val src = File("text.txt").absoluteFile
val text = "Kotlin is cool!!"
src.writeText(text)
```

* **File.appendText(String, Charset)**
* **File.appendText(String, String)**  
	ファイルにテキストを追記する拡張関数。

```kotlin
val src = File("log.txt").absoluteFile
val text = "Kotlin is cool!!"
src.appendText(text)
```

* **File.reader()**  
	FileからFileReaderを生成する拡張関数。

```kotlin
val file = File("text.txt").absoluteFile
val reader = file.reader()
// FileReaderで何かする
```

* **File.bufferedReader(Int)**  
	FileからBufferedReaderを生成する拡張関数。

```kotlin
val file = File("text.txt").absoluteFile
val reader = file.bufferedReader()
// BufferedReaderで何かする
```

* **File.inputStream()**  
	FileからInputStreamを生成する拡張関数。

```kotlin
val file = File("text.txt").absoluteFile
val is = file.inputStream()
// InputStreamで何かする
```

※ Writerに変換するものと、OutputStreamに変換する拡張関数がありますが、似たようなものが多くなるので省略します。

#### file.ReadWrite.kt

```text.txt
Kotlin1

Kotlin2
```

*  **&lt;T: Closeable&gt; T.use**  
	自動closeするインライン拡張関数。

```kotlin
val textFile = File("text.txt").absoluteFile
textFile.bufferedReader().use() {
	// テキストファイルの読み込みなど
}
// closeに失敗した場合は例外がスローされます。
```

※ 将来的にkotlinパッケージに移動し、usingという名前になるようです。

> TODO: Move to kotlin package, rename to using

* **BufferedReader.lineSequence(): Sequence&lt;String&gt;**  
	BufferedReaderからString型のシーケンスを生成する拡張関数。

```kotlin
File("text.txt").bufferedReader().use {
	it.lineSequence()
		.filter(String::isNotBlank)
		.forEach { println(it) }
}
// >[Kotlin1, Kotlin2]
```

* **&lt;T&gt; Reader.useLines((Sequence<String>) -> T): T**  
	String型のシーケンスを生成する受け取り、自動closeする拡張関数。

```kotlin
val textFile = File("text.txt")
val lines = textFile.reader().useLines {
	it.filter(String::isNotBlank).toList()
}
// >[Kotlin1, Kotlin2]
```

* **URL.readText(Charset)**
* **URL.readText(String)**  
	URLからテキストを直接読み込む拡張関数。

```kotlin
val url = URL("http://weather.livedoor.com/forecast/webservice/json/v1?city=400040")
println("response: ${url.readText()}")
// >response: {"pinpointLocations":[{"link": ... 略
```

#### IOStreams.kt

*  **InputStream.copyTo(OutputStream, Int): Long**  
	InputStreamからOutputStreamへコピーする拡張関数。

```kotlin
val src: InputStream = ... // 何かしらの方法でInputStream生成
val dest: OutputSteram = ... // 何かしらの方法でOutputStream生成
src.use { it.copyTo(dest) }
```

* **InputStream.reader(Charset): InputStreamReader**
* **InputStream.reader(String): InputStreamReader**  
	InputStreamからInputStreamReaderを生成する拡張関数。

```kotlin
val is: InputStream = ... // 何かしらの方法でInputStream生成
is.reader().use { it.readText() } // テキストの読み込みなど
```

* **InputStream.bufferedReader(Charset): BufferedReader**
* **InputStream.bufferedReader(String): BufferedReader**  
	InputStreamからBufferedReaderを生成する拡張関数。

```kotlin
val is: InputStream = ... // 何かしらの方法でInputStream生成
is.bufferedReader().use { it.readText() } // テキストの読み込みなど
```

※OutputStreamの拡張関数もありますが、似たようなものが多くなるので省略します。

### kotlin.text

#### regex.RegexJVM.kt

* **Regex**  
	正規表現のためのクラス。

```kotlin
val text = "KotlinかわいいよKotlin"
val regex = Regex("Kotlin")

// 一つ以上マッチするか
regex.containsMatchIn(text) // >true
// 全体とマッチするか
regex.matches(text) // >false

// マッチした文字列を取得する。
regex.findAll(text)
	.map { it.value }
	.forEach { println("matched: $it") }
	
// >matched: Kotlin
// >matched: Kotlin
```

* Javaバージョン

```kotlin
val text = "KotlinかわいいよKotlin"

val pattern = Pattern.compile("(Kotlin)")
val matcher = pattern.matcher(text)

// 一つ以上マッチするか
matcher.find()
// 全体とマッチするか
matcher.matches()

// マッチした文字列を取得する。
while (matcher.find()) {
	 println("matched: ${matcher.group()}")
}

// >matched: Kotlin
// >matched: Kotlin
```

#### regex.RegexExtensions

* **String.toRegex(): Regex**  
	文字列からRegexオブジェクトを生成する拡張関数。

```kotlin
val text = "KotlinかわいいよKotlin"

if ("^Kotlin".toRegex().containsMatchIn(text)) {
	println("start with: Kotlin")
}
// >start with: Kotlin

 if (text.contains("Kotlin$".toRegex())) {
	 println("end with: Kotlin")
 }
// >end with: Kotlin
```
#### Charsets.kt

* **Charsets**  
	標準的なCharsetを定義しているオブジェクトクラス。

```kotlin
// 標準で定義されているCharset。
arrayOf(
        Charsets.UTF_8,
        Charsets.UTF_16,
        Charsets.UTF_16BE,
        Charsets.UTF_16LE,
        Charsets.US_ASCII,
        Charsets.ISO_8859_1
).forEach { 
    println(it.displayName())
}

// >UTF-8
// >UTF-16
// >UTF-16BE
// >UTF-16LE
// >US-ASCII
// >ISO-8859-1
```

#### StringBuilder.kt

* **buildString(StringBuilder.() -> Unit): String**  
	StringBuilderの拡張関数+ラムダ式で文字列を生成するトップレベルインライン関数。

```kotlin
val string = buildString {
    append("Kotlin")
    append("かわいいよ")
    append("Kotlin")
}
println("build string: $string")
// >build string: KotlinかわいいよKotlin
```

* **StringBuilder.append(vararg String?): StringBuilder**  
	複数の文字列を追加する拡張関数。

```kotlin
val string = buildString {
    append("Kotlin", "かわいいよ", "Kotlin")
}
println("build String: $string")
// >build string: KotlinかわいいよKotlin
```

* **StringBuilder.appendln(String?): StringBuilder**  
	文字列+改行を追加する拡張関数。

```kotlin
val string = buildString {
    appendln("Kotlin")
    appendln("かわいいよー")
    appendln("Kotlin")
}
println(string)

// >Kotlin
// >かわいいよー
// >Kotlin
```

※他にも、プリミティブな型に対する拡張関数があります。

#### Strings.kt

* **CharSequence?.isNullOrEmpty(): Boolean**  
	文字列がnullもしくは空かどうかチェックする拡張関数。

```kotlin
val nullableText: String? = null
val emptyText = ""

if (nullableText.isNullOrEmpty()) {
	println("Text is null.")
}
// >Text is null.

if (emptyText.isNullOrEmpty()) { // or isEmpty()
	println("Text is empty.")
}
// >Text is empty.
```

Androidの場合、TextUtilsを使用しなくても判定できます。

* **CharSequence?.isNullOrBlank(): Boolean**  
	文字列がnull、空文字、ホワイトスペースかどうかチェックする拡張関数。

```kotlin
val nullableBlankText: String? = null
val blankText = "          "

if (nullableBlankText.isNullOrBlank()) {
	println("Text is null.")
}
// >Text is null.

if (blankText .isNullOrBlank()) { // or isBlank()
        println("Text is blank.")
}
// >Text is blank.
```
*  **String.substring(IntRange)**  
	レンジ構文で文字列を切り出す拡張関数。

```kotlin
val text = "Kotlinっぽい"
val subText = text.substring(0..5)
println(subText) // >Kotlin
```

* **String.substringBefore(String, String): String**  
	先頭から **最初** のデリミターまでを切り出す拡張関数。

```kotlin
val text = "hoge,huga,piyo"
println("${text.substringBefore(",")}") // >hoge
```

* **String.substringAfter(String, String): String**  
	先頭から **最後** のデリミターまでを切り出す拡張関数。

```kotlin
val text = "hoge,huga,piyo"
println("${text.substringAfter(",")}") // >hoge,huga
```

**末尾** からヒットしたデリミターまでを切り出す~Lastもあります。

### kotlin.utils

#### Integers.kt

* **repeat(Int, (Int) -> Unit)**  
	指定回数処理を繰り返すトップレベルインライン関数。

```kotlin
repeat(2) {
    println("$it: Kotlinかわいいよ")
}
// >0: Kotlinかわいいよ
// >1: Kotlinかわいいよ
```

#### Preconditions.kt

* **require(Boolean, () -> Any): Unit**  
	条件がfalseの場合、IllegalArgumentException をスローするトップレベルインライン関数。

```kotlin
fun say(msg: String) {
    require(msg.isNotEmpty()) { "Message is empty!!!" }
    println(msg)
}

say("") // >IllegalArgumentException: Message is empty!!!
```

*  **&lt;T:Any&gt; requireNotNull(T?, () -> Any): T**  
	値がnullの場合、IllegalArgumentExceptionをスローするトップレベルインライン関数。

```kotlin
fun say(msg: String?) {
    val m = require(msg) { "Message is null!!!" }
    println(m)
}

say(null) // >IllegalArgumentException: Message is null!!!
```

* **check(Boolean, () -> Any): Unit**  
	条件がfalseの場合、IllegalStateExceptionをスローするトップレベルインライン関数。

```kotlin
class Message(val text: String) {
    fun send() {
        check(text.isNotEmpty()) { "Message is empty!!!" }
        println("Send: $text")
    }
}

Message("").send()	// >IllegalStateException: Message is empty!!!"
```

* **&lt;T:Any&gt; checkNotNull(T?, () -> Any): T**  
	条件がfalseの場合、IllegalStateException をスローするトップレベルインライン関数。

```kotlin
class Message(val text: String?) {
    fun send() {
        val text = checkNotNull(text) { "Message is NULL!!!" }
        println("Send: $text")
    }
}

Message(null).send()	// >IllegalStateException: Message is null!!!"
```

* **error(Any): Nothing**  
	IllegalStateExceptionをスローするトップレベル関数。

```kotlin
fun errorOccurred() {
	error("この方がシンプルだと思うの。") // >IllegalStateException: この方がシンプルだと思うの。
}
```

#### Standard.kt

* **NotImplementedError**  
	未実装の時にスローされる例外。

```kotlin
class ImplementedObject {
	fun do() = NotImplementedError("後で実装する")
}

ImplementedObject().do() // >NotImplementedError: 後で実装する
```

* **TODO(String): Nothing**  
	NotImplementedErrorをスローするトップレベル関数。

```kotlin
class ImplementedObject {
	fun do(): Int = TODO("後で実装する")
}

ImplementedObject().do() // >NotImplementedError: 後で実装する
```

#### SystemJVM.kt

* **measureTimeMillis(() -> Unit) : Long**  
	処理の経過時間をミリ秒で返すトップレベル関数。

```kotlin
val elapsed = measureTimeMillis {
    var counter = 0
    repeat(100000000) {
        counter++
    }
}
println("ElapsedTime: $elapsed milliseconds")
// >ElapsedTime: 1130 milliseconds
```

* **measureTimeNano(() -> Unit) : Long**  
	処理の経過時間をナノ秒で返すトップレベル関数。

```kotlin
val elapsed = measureTimeMillis {
    var counter = 0
    repeat(100000000) {
        counter++
    }
}
println("ElapsedTime: $elapsed nanoseconds")
// >ElapsedTime: 924146585 nanoseconds
```

#### Tuples.kt

* **Pair**  
	２つのイミュータブルな値を表すデータクラス。

```kotlin
val pair = Pair("Kotlin", "かわいい")
println("Pair: $pair") // >Pair: (Kotlin, かわいい)

// 呼び出し元をfirst, 引数をsecondにした新しいPairを返す。
val newPair = pair.to("かわいかった")
println("Pair: $newPair") // >Pair: ((Kotlin, かわいい), かわいかった)

val list = pair.toList()
println("List: $list") // >List: [Kotlin, かわいい]
```

dataクラスなので次のような使い方もできます。

```kotlin
fun makePair() = Pair("hoge", "huga")

val (hoge, huga) = makePair()
println("$hoge, $huga") // >hoge, huga
```

* **Triple**  
	3つのイミュータブルな値を表すデータクラス。

```kotlin
val triple = Triple("Kotlin", "は", "かわいい")
println("Triple: $triple") // >Triple: (Kotlin, は, かわいい)

val list = triple.toList()
println("List: $list") // >List: [Kotlin, は, かわいい]
```

### おまけ

#### Charsetの拡張

SJISなどほかの文字コードが必要な場合、Charsetsに拡張プロパティを追加すると良さ気です。

```kotlin
public val Charsets.SJIS: Charset
    get() = Charset.forName("SJIS")

println(Charsets.SJIS.displayName())　// >Shift_JIS
```

#### *null*チェックによる早期リターン

メンバ変数がnullの場合、Swiftのguardっぽく早期リターンできます。

```kotlin
class Message(val text: String?) {
    fun send(): Boolean {
        // 引数チェックの場合はrequireのほうがよさ気です（結果は同じ）。
        val text = checkNotNull(text) {
            println("Text is NULL!!!") 
            return false
        }
        println("Send: $text")
        return true
    }
}

Message(null).send() // > Text is NULL!!!
```

---

全体を眺めてみますとファイルの入出力周りや文字列周りが改善されていることが伺えます。Androidの開発だとAPIレベルによってJava7の機能が一部しか使えないことがあるのですが、Kotlinはそのあたり解決できますし、それ以外にも便利なものがあるので、標準ライブラリ積極的に使っていきたいですね。

明日はlaprasDrumさんです。

> Written with [StackEdit](https://stackedit.io/).