※ソース記事はこちら
※Kotlin/JSやKotlin/Nativeについては個人的に使っていないので、割愛します。
標準ライブラリの変更
追加された新しい機能は実験的な状態であることに注意。
共通ライブラリのStringBuilderの拡張
StringBuilderはすでに標準ライブラリ、kotlin.text
パッケージにあるが、多くの重要なメンバは無いか、JVMでのみ利用可能だった。現在は、JVMのすべてのStringBuilder機能は共通のexpect classに追加され、それぞれのプラットフォーム上で対応する実装を持つ。これはすべての必要なメンバがそこにあるため、共通コードからStringBuilder
を効果的に使用することができることを意味する。
KClassとの連携
KClassについての基本的に有用なメンバは、kotlin-reflectへの依存が不要になった。
import kotlin.reflect.cast
@OptIn(ExperimentalStdlibApi::class)
fun main() {
val kClass = String::class
println(kClass.simpleName) // String
println(kClass.qualifiedName) // kotlin.String
println(kClass.isInstance("abc")) // true
println(kClass.isInstance(10)) // false
println(kClass.cast("abc")) // abc
}
以前は、動作させるため、実行時にKotlinのリフレクションの実装を提供する必要があった。
今では追加の依存関係無しで、これらのシンプルなメンバーを使用することができる。
実験的アノテーションを改名(@Experimentalと@UseExperimental)
ご存じかもしれないが、Kotlinには実験的機能を使用するためのメカニズムが備わっている。それは標準ライブラリからのアノテーションを含む。それは自身が実験的であるか、他の実験的宣言を使用することの印を宣言につける。以前のバージョンではそれらは、@UseExperimental
と@Experimental
だった。
このメカニズムのスコープを広げることが決定された。なぜなら実験的状態であるものは、APIを使うために同意を必要とする唯一の理由ではないためである。例えば、あるAPIは内部向けであるとか、いくつか制約があるといったことがありうる。これを反映させるため、アノテーションを改名した。@UseExperimentalと@Experimentが@OptInと@RequiresOptInに変更された。したがってコンパイラ引数の-Xuse-experimentalも、-Xopt-inに変更された。-Xexperimentalについては、まれにしか使わず、複雑になるので、削除されている。@UseExperimentalと@Experimentalは1.3.70ではまだサポートされるが、1.4にて削除する予定である。
時間計測APIの改名
もう一つの重要な改名は、期間と時間計測APIである。Clock
とClockMark
はTimeSource
とTImeMark
にそれぞれ改名された。以前の名前は非推奨の型エイリアスとして維持されている。
e)
ArrayDeque - 両端キューの実装
両端キューの実装である、kotlin.collections.ArrayDeque
クラスが標準ライブラリに追加されたことをうれしく思う。コミュニティから何度か要求されていた。たとえJava標準ライブラリのjava.util.ArrayDequeが使えるとしても、Kotlin/JS、Kotlin/Nativeのために使える共通の実装が無く、重要なのは共通のコードに無いことだった。現在は、実験的状態とはいえ、そのような実装が利用可能である。
両端キューは、償却定数時間で、キューの先端・末端両方向から要素を追加・削除可能である。
@OptIn(ExperimentalStdlibApi::class)
fun main() {
val deque = ArrayDeque(listOf(1, 2, 3))
deque.addFirst(0)
deque.addLast(4)
println(deque) // [0, 1, 2, 3, 4]
println(deque.first()) // 0
println(deque.last()) // 4
deque.removeFirst()
deque.removeLast()
println(deque) // [1, 2, 3]
}
キューやスタックがコードで必要になったら、デフォルトで両端キューを使うことができる。
kotlin.collections.ArrayDeque
の実装は、可変長のArrayをベースに使い、循環バッファであるArrayの中に中身を入れ、一杯になった時だけ、リサイズする。
コンセプトとしては、ArrayDeque
の実装はjava.util.ArrayDeque
にととても良く似ている。しかし注意点として別の実装であり、この新しい実装は、Kotlin/JVMのためにこのクラスを使うときに使われる。この違いは、次のように他のCollectionと連携するときに現れる。ArrayList
を生成しJVMでコンパイルすると、実際はjava.util.ArrayList
が使われる。JavaのArrayDeque
がCollection
インターフェイスのみを実装しているのと異なり、KotlinのArrayDeque
はMutableList
を実装している。このことはインデックスですべての要素にアクセスでき、JavaのArrayDequeでは不可能である。
ArrayDequeクラスは現状は実験的状態でリリースされており、フィードバックを楽しみにまっている。
Collectionビルダー
もう一つの重要な機能は、コレクションのためのbuilder関数、つまりbuildList
、buildSet
、buildMap
である。builder関数を使うことで、生成の間は変更可能なコレクションを便利に操作し、結果として読み取り専用のコレクションを得ることができる。
@OptIn(ExperimentalStdlibApi::class)
fun main() {
val needsZero = true
val initial = listOf(2, 6, 41)
val ints = buildList { // this: MutableList
if (needsZero) {
add(0)
}
initial.mapTo(this) { it + 1 }
}
println(ints) // [0, 3, 7, 42]
}
これらのbuilder関数は、buildStringと似たように実装されている。buildListは引数として一つのレシーバーを持つラムダ関数を取る。ラムダ関数の中の暗黙のthisレシーバーは、MutableListの型を持ち、buildListは戻り値として読み取り専用のListを返却する。
builder関数を使うことは、条件を使ったり、いくつかの初期状態のコレクションを変更したり、結果をマージしたりといった複雑な操作を行う必要があるときに、パフォーマンス面で度々読みやすく効果的である。これらの関数は現状実験的状態でリリースされることに注意すること。
reduceOrNullとrandomOrNull対応物
Kotlinの規約として、string.toInt()
とstring.toIntOrNull()
のように、関数のペア、一つは操作ができないなら例外を投げるもの、もう一つはnullを返却するものがある。今回新しく、対応物としてrandomOrNull()
とreduceOrNull()
関数が同じ規約にしたって追加された。
@OptIn(ExperimentalStdlibApi::class)
fun main() {
val list = listOf(1, 2, 3)
println(list.randomOrNull()) // 1 or 2 or 3
println(list.reduceOrNull { a, b -> a + b }) // 6
val emptyList = emptyList<Int>()
println(emptyList.randomOrNull()) // null
println(emptyList.reduceOrNull { a, b -> a + b }) // null
}
もしramdon()
やreduce()
を使うなら、コレクションが空の場合は例外が発生するだろう。
scan()関数
リストとシーケンスに連携する新しい一連の関数が追加されている。それらは「スキャン」の概念を意味し、似たような関数は、異なるライブラリや言語にすでに存在する。
scan()
はfold()
に密接に関連している。scan()
もfold()
もどちらも一連の値への二値の操作を適用するが、fold()
が最終的な結果を返却するのに対して、scan()
は中間結果のシーケンスを返却する。
@OptIn(ExperimentalStdlibApi::class)
fun main() {
val ints = (1..4).asSequence()
println(ints.fold(0) { acc, elem -> acc + elem }) // 10
val sequence = ints.scan(0) { acc, elem -> acc + elem }
println(sequence.toList()) // [0, 1, 3, 6, 10] ここでscan処理が行われる
}
もし結果の型が要素の型と同じであり、初期値として最初の要素を使うことができるのなら、reduce()
とscanReduce()
が使うことができる。
留意すべきは、シーケンスにおいてscan()
とscanReduce()
を使うときは、それらは結果としてシーケンスを返却するが、本来の性質として遅延が行われる。このことは、scanの結果のsequenceからデータを問い合わせるとき、具体的には、例えば、toList()
やmax()
といった、(他のシーケンスでない何かを返却する)最終的な操作を呼ぶときに、scan()
の処理が始まることを意味する。リストにおいてscan()
とscanReduce()
を使うときは、結果としてリストが返却される。
intelliJ IDEAサポート(※割愛)
Kotlin/JVM
Kotlinは今はJVMバイトコード(version1.8+ターゲット)内に型アノテーションを生成するため、実行時に使うことができる。この特徴は何度かコミュニティによって要求があった。なぜなら、既存のJavaライブラリをより簡単に扱うことができ、新しいライブラリを作るうえで強力なためである。
以下の例では、String上の@Fooアノテーションは、バイトコードに出力され、ライブラリコードにより使うことができる。
@Target(AnnotationTarget.TYPE)
annotation class Foo
class A {
fun foo(): @Foo String = "OK"
}
バイトコードに型アノテーションを出力するには、次の手順にしたがうこと。
- 宣言したアノテーションが、適切なアノテーション対象(JavaのElementType.TYPE_USEかKotlinのAnnotationTarget.TYPE)とretention(AnnotationRetention.RUTIME)であることを確認する。
- クラス宣言のアノテーションとアノテーションを使うコード両方が、JVMバイトコードのターゲットバージョンが1.8以上でコンパイルする。コンパイラオプションで-jvm-target=1.8で指定できる。
- アノテーションを使うコードを-Xemit-jvm-type-annotationsコンパイラオプションでコンパイルする。
注意すべきは、標準ライブラリからの型アノテーションは、ターゲットバージョン1.6でコンパイルされているため、現状、バイトコードには出力されない。
今のところ、以下の基本的なケースのみサポートされている。 - メソッドパラメータ、戻り値、プロパティ型の型アノテーション
- 不変な型引数の射影 例えば
Smth<@Ann Foo>, Array<@Ann Foo>
※)不変とは、上記例でFooそのものの型であり、Fooの基底クラスや派生クラスは不可。それらを許可することは共変や反変という。
将来的には 共変や反変の型射影の型アノテーションや、インナー型のアノテーション(Smth<@Ann Outer.Inner>
のようなもの)のサポートを計画している。サポートされているケースですでにほぼ、実世界のシナリオをカバーしていると信じているが、もしより複雑なケースで型アノテーションが必要ならば、教えてほしい。
更新方法
いつも通り、こちらでオンラインでKotlinを試すことができる。
-
GradleとMaven: コンパイラと標準ライブラリのためのバージョンとして
1.3.70
を使用すること。Gradle用とMaven用のドキュメントを参照のこと。 - IntelliJ IDEAとAndroid Studio: Kotlinプラグインのバージョンを1.3.70に更新すること。ツール/Kotlin/Kotlinプラグインの更新の構成を使い、「再度チェックする」ボタンをクリックすること。
- Eclipse: MarketPlaceを使ってプラグインをインストールすること。
-
コマンドラインコンパイラ:[Github リリースページ]からダウンロードすることができる。
もし、新しいリリースで問題に遭遇したら、Slack(ここで招待を得る)のフォーラムで自由に質問するか、issue trackerで問題を報告することができる。