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
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
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.