1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Kotlin 1.3.70の変更点

Last updated at Posted at 2022-04-29

※ソース記事はこちら
※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である。ClockClockMarkTimeSourceTImeMarkにそれぞれ改名された。以前の名前は非推奨の型エイリアスとして維持されている。
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のArrayDequeCollectionインターフェイスのみを実装しているのと異なり、KotlinのArrayDequeMutableListを実装している。このことはインデックスですべての要素にアクセスでき、JavaのArrayDequeでは不可能である。
ArrayDequeクラスは現状は実験的状態でリリースされており、フィードバックを楽しみにまっている。

Collectionビルダー

もう一つの重要な機能は、コレクションのためのbuilder関数、つまりbuildListbuildSetbuildMapである。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で問題を報告することができる。
1
1
0

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

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?