LoginSignup
233
229

More than 5 years have passed since last update.

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

Last updated at Posted at 2015-12-18

Kotlin Advent Calendar 2015の19日目の記事です。
昨日は @magie-poohさんの業務でKotlinを書いている僕がKotlinを書く際に個人的に注意していることでした。

Kotlinは標準ライブラリとして色々便利な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
    ラムダ式でスレッド生成するトップレベル関数。
// デフォルトでは生成直後にスタート
thread {
    Thread.sleep(3000L) 
}

// 省略したデフォルト引数
thread(start = true, 
       isDaemon = true, 
       contextClassLoader = null, 
       name = null, 
       priority = -1) {
}
  • currentThread: Thread
    現在のスレッドトップレを取得するトップレベルプロパティ。
// メインスレッドかどうかの判定
currentThread == Looper.getMainLooper().getThread()

kotlin.io

files.Utils.kt

  • File.copyTo(File, Boolean, Int): Long
    ファイルをコピーする拡張関数。
val src = File("text.txt")
val dest = File("dest/text.txt")
src.copyTo(dest, overwrite = true)
  • File.copyRecursively(File, (File, IOException) -> OnErrorAction): Boolean
    ディレクトリごとコピーするする拡張関数。
val src = File("src")
val dest = File("dest")
src.copyRecursively(dest) { file, error ->
    // コピーエラー
    OnErrorAction.SKIP // or TERMINATE
}
  • File.deleteRecursively(): Boolean
    ディレクトリごと削除する拡張関数。
val dir = File("to_delete")
val success = dir.deleteRecursively()
  • File.extension: String
    ファイルの拡張子を返す拡張プロパティ。
val textFile = File("text.txt").absoluteFile
println("extension: ${text.extension}") // >extension: txt
  • File.nameWithoutExtension: String
    ファイル名のみ返す拡張プロパティ。
val text = File("text.txt").absoluteFile
println("filename: ${textFile.nameWithoutExtension}") // >filename: text
  • File.listFiles((File) -> Boolean)
    File#listFiles(FileFilter)の高階関数版の拡張関数。
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)
    ファイル内容を文字列のリスト(行単位)で返す拡張関数。
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)
    ファイル内容を行単位で取得する拡張関数
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)
    ファイルにテキストを書き込む拡張関数。
val src = File("text.txt").absoluteFile
val text = "Kotlin is cool!!"
src.writeText(text)
  • File.appendText(String, Charset)
  • File.appendText(String, String)
    ファイルにテキストを追記する拡張関数。
val src = File("log.txt").absoluteFile
val text = "Kotlin is cool!!"
src.appendText(text)
  • File.reader()
    FileからFileReaderを生成する拡張関数。
val file = File("text.txt").absoluteFile
val reader = file.reader()
// FileReaderで何かする
  • File.bufferedReader(Int)
    FileからBufferedReaderを生成する拡張関数。
val file = File("text.txt").absoluteFile
val reader = file.bufferedReader()
// BufferedReaderで何かする
  • File.inputStream()
    FileからInputStreamを生成する拡張関数。
val file = File("text.txt").absoluteFile
val is = file.inputStream()
// InputStreamで何かする

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

file.ReadWrite.kt

text.txt
Kotlin1

Kotlin2
  • <T: Closeable> T.use
    自動closeするインライン拡張関数。
val textFile = File("text.txt").absoluteFile
textFile.bufferedReader().use() {
    // テキストファイルの読み込みなど
}
// closeに失敗した場合は例外がスローされます。

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

TODO: Move to kotlin package, rename to using

  • BufferedReader.lineSequence(): Sequence<String>
    BufferedReaderからString型のシーケンスを生成する拡張関数。
File("text.txt").bufferedReader().use {
    it.lineSequence()
        .filter(String::isNotBlank)
        .forEach { println(it) }
}
// >[Kotlin1, Kotlin2]
  • <T> Reader.useLines((Sequence) -> T): T
    String型のシーケンスを生成する受け取り、自動closeする拡張関数。
val textFile = File("text.txt")
val lines = textFile.reader().useLines {
    it.filter(String::isNotBlank).toList()
}
// >[Kotlin1, Kotlin2]
  • URL.readText(Charset)
  • URL.readText(String)
    URLからテキストを直接読み込む拡張関数。
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へコピーする拡張関数。
val src: InputStream = ... // 何かしらの方法でInputStream生成
val dest: OutputSteram = ... // 何かしらの方法でOutputStream生成
src.use { it.copyTo(dest) }
  • InputStream.reader(Charset): InputStreamReader
  • InputStream.reader(String): InputStreamReader
    InputStreamからInputStreamReaderを生成する拡張関数。
val is: InputStream = ... // 何かしらの方法でInputStream生成
is.reader().use { it.readText() } // テキストの読み込みなど
  • InputStream.bufferedReader(Charset): BufferedReader
  • InputStream.bufferedReader(String): BufferedReader
    InputStreamからBufferedReaderを生成する拡張関数。
val is: InputStream = ... // 何かしらの方法でInputStream生成
is.bufferedReader().use { it.readText() } // テキストの読み込みなど

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

kotlin.text

regex.RegexJVM.kt

  • Regex
    正規表現のためのクラス。
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バージョン
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オブジェクトを生成する拡張関数。
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を定義しているオブジェクトクラス。
// 標準で定義されている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の拡張関数+ラムダ式で文字列を生成するトップレベルインライン関数。
val string = buildString {
    append("Kotlin")
    append("かわいいよ")
    append("Kotlin")
}
println("build string: $string")
// >build string: KotlinかわいいよKotlin
  • StringBuilder.append(vararg String?): StringBuilder
    複数の文字列を追加する拡張関数。
val string = buildString {
    append("Kotlin", "かわいいよ", "Kotlin")
}
println("build String: $string")
// >build string: KotlinかわいいよKotlin
  • StringBuilder.appendln(String?): StringBuilder
    文字列+改行を追加する拡張関数。
val string = buildString {
    appendln("Kotlin")
    appendln("かわいいよー")
    appendln("Kotlin")
}
println(string)

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

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

Strings.kt

  • CharSequence?.isNullOrEmpty(): Boolean
    文字列がnullもしくは空かどうかチェックする拡張関数。
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、空文字、ホワイトスペースかどうかチェックする拡張関数。
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)
    レンジ構文で文字列を切り出す拡張関数。
val text = "Kotlinっぽい"
val subText = text.substring(0..5)
println(subText) // >Kotlin
  • String.substringBefore(String, String): String
    先頭から 最初 のデリミターまでを切り出す拡張関数。
val text = "hoge,huga,piyo"
println("${text.substringBefore(",")}") // >hoge
  • String.substringAfter(String, String): String
    先頭から 最後 のデリミターまでを切り出す拡張関数。
val text = "hoge,huga,piyo"
println("${text.substringAfter(",")}") // >hoge,huga

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

kotlin.utils

Integers.kt

  • repeat(Int, (Int) -> Unit)
    指定回数処理を繰り返すトップレベルインライン関数。
repeat(2) {
    println("$it: Kotlinかわいいよ")
}
// >0: Kotlinかわいいよ
// >1: Kotlinかわいいよ

Preconditions.kt

  • require(Boolean, () -> Any): Unit
    条件がfalseの場合、IllegalArgumentException をスローするトップレベルインライン関数。
fun say(msg: String) {
    require(msg.isNotEmpty()) { "Message is empty!!!" }
    println(msg)
}

say("") // >IllegalArgumentException: Message is empty!!!
  • <T:Any> requireNotNull(T?, () -> Any): T
    値がnullの場合、IllegalArgumentExceptionをスローするトップレベルインライン関数。
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をスローするトップレベルインライン関数。
class Message(val text: String) {
    fun send() {
        check(text.isNotEmpty()) { "Message is empty!!!" }
        println("Send: $text")
    }
}

Message("").send()  // >IllegalStateException: Message is empty!!!"
  • <T:Any> checkNotNull(T?, () -> Any): T
    条件がfalseの場合、IllegalStateException をスローするトップレベルインライン関数。
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をスローするトップレベル関数。
fun errorOccurred() {
    error("この方がシンプルだと思うの。") // >IllegalStateException: この方がシンプルだと思うの。
}

Standard.kt

  • NotImplementedError
    未実装の時にスローされる例外。
class ImplementedObject {
    fun do() = NotImplementedError("後で実装する")
}

ImplementedObject().do() // >NotImplementedError: 後で実装する
  • TODO(String): Nothing
    NotImplementedErrorをスローするトップレベル関数。
class ImplementedObject {
    fun do(): Int = TODO("後で実装する")
}

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

SystemJVM.kt

  • measureTimeMillis(() -> Unit) : Long
    処理の経過時間をミリ秒で返すトップレベル関数。
val elapsed = measureTimeMillis {
    var counter = 0
    repeat(100000000) {
        counter++
    }
}
println("ElapsedTime: $elapsed milliseconds")
// >ElapsedTime: 1130 milliseconds
  • measureTimeNano(() -> Unit) : Long
    処理の経過時間をナノ秒で返すトップレベル関数。
val elapsed = measureTimeMillis {
    var counter = 0
    repeat(100000000) {
        counter++
    }
}
println("ElapsedTime: $elapsed nanoseconds")
// >ElapsedTime: 924146585 nanoseconds

Tuples.kt

  • Pair
    2つのイミュータブルな値を表すデータクラス。
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クラスなので次のような使い方もできます。

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

val (hoge, huga) = makePair()
println("$hoge, $huga") // >hoge, huga
  • Triple
    3つのイミュータブルな値を表すデータクラス。
val triple = Triple("Kotlin", "は", "かわいい")
println("Triple: $triple") // >Triple: (Kotlin, は, かわいい)

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

おまけ

Charsetの拡張

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

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

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

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

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

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.

233
229
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
233
229