コトリンの現在の開発


先週、 Redmadrobot SPBの支援により、 Ketlin User Group SPBの一部として、JetBrainsの開発者であるStanislav Erokhinとのミーティングが開催されました。 会議で、彼はKotlinの次のメジャーバージョン(1.3未満)の開発機会に関する情報を共有しました。


この記事では、受け取った資料をまとめ、計画について話します。


重要です。 説明するすべての機能の設計は、認識を超えて変更できます。 以下はすべてチームの現在の計画ですが、開発者はいかなる保証も行いません。


YouTubeリンクでレポートを見ることができます


はじめに


Kotlinの次のメジャーバージョン(1.3)では、多くの変更が予定されています。 彼らはそのうちのいくつかについてだけ語ってくれました。


主な方向:



コルーチン


バージョン1.3では、待望のKotlinでのコルチンのリリースが計画されており、その結果、 kotlin.coroutines.experimentalパッケージからkotlin.coroutinesに移行します。


後で、既存のCorutin APIをサポートするサポートライブラリをリリースする予定です。


コルーチンの大きな変更



現時点では、 Continuationは結果またはエラーのいずれかを転送できる2つのメソッドが含まれています。


 interface Continuation<in T> { fun resume(value: T) fun resumeWithException(exception: Throwable) } 

新しいバージョンでは、何らかの形式の値またはエラーを含む型指定されたResultクラスを使用する予定であり、 Continuationでは1つのresume(result: Result<T>)メソッドをresume(result: Result<T>)


 class Result<out T> { val value: T? val exception: Throwable? } interface Continuation<in T> { fun resume(result: Result<T>) } 

開発者は各値/エラー転送の前にチェックを実行する必要がないため、このような実装により、コルーチン用のライブラリを開発するのがより便利になります。 このソリューションのAPIは間違いなく変更されますが、意味は変わりません。


残念ながら、このソリューションには問題があります。 Kotlinの現在の実装ですべてが機能するためには、各値をラッパーResultでラップする必要があります。これにより、 resume呼び出しごとに追加のオブジェクトが作成されます。 この問題の解決策は、 インラインクラスです。


インラインクラス


Result例:


 inline class Result<out T>(val o: Any?) { val value: T? get() = if (o is Box) null else o as T val exception: Throwable? get() = (o as? Box).exception } private class Box(val exception: Throwable) 

インラインクラスの利点は、上記の例に従って、実行時にオブジェクトがコンストラクタに渡されたval oとして単純に保存され、 exceptionBoxラッパーに残ることです。 これは、エラーが非常にまれにしかスローされず、ラッパーの生成がパフォーマンスに影響しないためです。 ゲッターvalueexceptionは静的メソッドに生成され、ラッパー自体は消えます。 これにより、 Continuation.resume呼び出すときに冗長オブジェクトを作成する問題が解決されます。


インラインクラスの制限



符号なし算術


インラインクラスの外観の考えられる結果の1つは、 符号なし型です。


 inline class UInt(val i: Int) inline class ULong(val l: Long) ... 

標準ライブラリでは、各プリミティブについて、対応する符号なしバリアントが記述されます。このバリアントでは、算術演算子を使用した作業が実装されるほか、 toString()などの基本メソッドも実装されます。


リテラルサポートと暗黙的な変換も追加されます。


デフォルトの方法


標準実装のメソッドは、Javaよりも早くKotlinのインターフェースに登場しました。
インターフェイスがKotlinで記述され、実装がJava 8である場合、標準実装のメソッドでは@JvmDefaultアノテーションを使用できる@JvmDefault 、Javaの観点からこのメソッドはdefaultとしてマークされdefault


DFAおよびスマートキャストのメタ情報


開発中のもう1つの興味深い点は、DFA(データフロー分析)およびスマートキャストのメタ情報を追加できる契約です。 例を考えてみましょう:


 fun test(x: Any) { check(x is String) println(x.length) } 

check(Boolean)関数は、 x String型であることを確認した結果を渡します。 このレベルでは、この関数が内部で何を行うかは不明であるため、次のコード行でエラーが発生します(この場合、 スマートキャストは不可能です)。 コンパイラは、 xStringあることを確認できません。 契約は彼がこれを説明するのを助けます。


コントラクトでのcheck実装は次のとおりです。


 fun check(value: Boolean) { contract { returns() implies value } if (!value) throw ... } 

このメソッドが結果を返す場合、この結果は正確にtrueというかなり具体的な構文で、 contract関数呼び出しがここに追加されました。 そうでない場合、例外がスローされます。


このような契約のおかげで、コンパイラーはxに対してスマートキャストを実行でき、 println(x.length)呼び出してもエラーは発生しません。


不明なタイプの状況を解決することに加えて、契約は他の問題を解決します。 例:


 fun test() { val x: String run { x = "Hello" } println(x.length) } fun <R> run(block: () -> R): R { contract { callsInPlace(block, EXACTLY_ONCE) } return block() } 

run関数のコントラクトは、渡されたblockが1回だけ呼び出され、定数x正しく初期化され、 x.lengthがエラーなしで実行されることをコンパイラに伝えます。


コントラクトはコンパイラーによって使用されますが、コンパイルの結果のコードには影響しません。 すべてのコントラクトはメタ情報に変換され、特定の関数のいくつかの仮定をコンパイラーに通知します。


IDEは、可能な場合、契約と自動生成の特定の強調表示を計画します。


型推論を制御するための注釈


Kotlin stdlibには、内部でのみ使用されるさまざまな機能が既にあります。 開発者はそれらのいくつかを共有する準備ができています。 新しいアノテーションとそれらが提供する機能を検討してください。


@NoInfer


@NoInferは、 型推論を少し賢くするように設計されています。


filterIsInstanceメソッドを使用してコレクションをフィルタリングするとします。 このメソッドでは、フィルター処理を実行する型を指定することが重要ですが、コンパイラーは型を登録せずに呼び出しを許可できます(推定しようとします)。 署名で@NoInferを使用すると、タイプのない呼び出しはエラーとして目立ちます。


 fun print(c: Collection<Any>) { ... } val c: Collection<Any> print(c.filterIsInstance()) // print(c.filterIsInstance<String>()) fun <R> Iterable<*>.filterIsInstance(): List<@NoInfer R> 

@Exact


この注釈は@NoInferに非常に似てい@NoInfer 。 彼女は、型が指定されたものと正確に等しくなければならない、つまり「サブタイプ」でも「スーパータイプ」でもないことを報告します。


@OnlyInputTypes


この注釈は、ユーザーが引数またはレシーバーに持っていたタイプのみが推論結果であることを示します。


SAM for Javaメソッド


開発中の別の機能により、SAMの操作が簡単になります。 例を考えてみましょう:


 //Java static void test(Factory f, Runnable r) interface Factory { String produce(); } //Kotlin fun use(f: Factory) { test(f) { } // Factory != () -> String } 

Javaは、2つの機能インターフェイスを受け入れるメソッドを宣言します。 Kotlinレベルでは、インターフェイスの実装オブジェクトと、これらのインターフェイスのメソッドのシグネチャに対応するラムダ式の両方を任意の組み合わせで渡して、このメソッドを呼び出すことができるはずです。


 1. test(Factory, Runnable) 2. test(Factory, () -> Unit) 3. test(() -> String, Runnable) 4. test(() -> String, () -> Unit) 

現在、オプション2と3は不可能です。 Kotlinコンパイラは、2つのオプションのみを許可します。1つは2つのインターフェイスを受け入れ、もう1つは2つのラムダを受け入れます。


コンパイラの現在の実装で4つのオプションすべてを可能にすることは難しい作業ですが、不可能ではありません。 新しいタイプの推論システムは、このような状況をサポートします。 環境では1つのfun test(Factory, Runnable)関数のみが表示されますが、ラムダとインターフェイス実装オブジェクトの両方を任意の組み合わせで転送できます。


Kotlinメソッドおよびインターフェイス用のSAM


Kotlinインターフェースを受け入れるメソッドがKotlinで受け入れられる場合、自動変換を行うには(インターフェースの実装とラムダの両方を転送できます)、キーワードsamインターフェースをマークする必要があります。


 sam interface Predicate<T> { fun test(t: T): Boolean } fun Collection<T>.filter(p: Predicate<T>): Collection<T> { ... } fun use() { test { println("Hello") } val l = listOf(-1, 2, 3) l.filter { it > 0 } } 

Javaインターフェースの場合、変換は常に機能します。


ビルダー向けのスマートな推論


ビルダーを書きたいと想像してください:


 fun <T> buildList(l: MutableList<T>.() -> Unit): List<T> { ... } 

そして、次のように使用します。


 val list = buildList { add("one") add("two") } 

TT add(String)呼び出しから推測されないため、現時点ではこれは不可能です。 したがって、次のように記述する必要があります。


 val list = buildList<String> { add("one") add("two") } 

最初のオプションを(タイプを明示的に指定せずに)使用できるようにする予定です。


コンパイラスキーマの変更


JetBrainsは、Kotlin Nativeに積極的に取り組んでいます。 その結果、別のリンクがコンパイラスキームに登場しました- バックエンド内部表現(BE IR)



BE IRは、ソースコードのすべてのセマンティクスを含む中間表現であり、システムのバイナリコードを含む、任意のプラットフォームの実行可能ファイル用にコンパイルできます。 現在、BE IRはKotlin Nativeでのみ使用されていますが、セマンティクスに関する追加情報を含むPSIではなく、すべてのプラットフォームで使用される予定です。 JVMとJSのプロトタイプは既にあり、それらは積極的に開発されています。


その結果、すべてのソースコードがBE IRに変換され、次にターゲットプラットフォームの実行可能ファイルに変換されます。


まとめ


上に書いたように、どのイノベーションがリリースに到達するか、どのような形式、どのバージョンの言語であるかはわかりません。 Kotlinチームの現在の計画のみがありますが、 開発者は保証しません



Kotlinは、間違いなく確立されたプログラミング言語ですが、ユーザーベースの増加に伴い、そのアプリケーションの範囲は拡大しています。 新しい要件、プラットフォーム、およびユースケースが出現しています。 維持したい古いコードが蓄積されています。 この言語の開発者は困難や問題を理解しており、その結果、新しいバージョンではより多くの状況でそれらを使用することがさらに便利になります。



Source: https://habr.com/ru/post/J351516/


All Articles