今日、多くの人がリアクティブプログラミングを愛しています。 これには多くの利点があります。いわゆる「 コールバック地獄 」の欠如、組み込みのエラー処理メカニズム、およびバグの可能性を減らす関数型プログラミングスタイルです。 マルチスレッドコードの記述が大幅に簡単になり、データストリームの管理(結合、分割、変換)が簡単になります。
多くのプログラミング言語には、JVM用のRxJava、JavaScript用のRxJS、iOS用のRxSwift、Rx.NETなど、独自のリアクティブライブラリがあります。
しかし、Kotlinには何がありますか? RxKotlinと仮定するのは論理的です。 そして、実際、そのようなライブラリは存在しますが、RxJava2、いわゆる「砂糖」の単なる拡張セットです。
そして理想的には、私は次の基準を満たすソリューションを持ちたいと思います。
- マルチプラットフォーム-リアクティブプログラミングを使用してマルチプラットフォームライブラリを作成し、社内で配布できるようにする。
- ヌルの安全性 -Kotlin型システムは「 10億ドルのエラー 」から私たちを保護するため、ヌル値は有効でなければなりません(たとえば、
Observable<String?>
)。
- 共分散と反分散は、Kotlinの別の非常に便利な機能であり、たとえば
Observable<String>
型をObservable<CharSequence>
型に安全にキャストできます。
Badooの私たちは、海辺の天気を待たないことを決め、そのような図書館を作りました。 ご想像のとおり、これをReaktiveと呼び、 GitHubに投稿しました。
この記事では、Kotlinの事後対応型のプログラミングの期待を詳しく見て、Reaktiveの機能がそれらにどのように適合するかを見ていきます。
3つの自然な恩恵
マルチプラットフォーム
最初の自然な利点は最も重要です。 現在、iOS、Android、およびモバイルWebチームは別々に存在しています。 要件は一般的であり、設計は同じですが、各チームが独自の作業を行います。
Kotlinを使用すると、マルチプラットフォームコードを作成できますが、リアクティブプログラミングは忘れてください。 そして、リアクティブプログラミングを使用して共有ライブラリを作成し、社内で配布するか、GitHubに配置できるようにしたいと考えています。 潜在的に、このアプローチは開発時間を大幅に短縮し、コードの総量を削減できます。
ヌルの安全性
それはむしろJavaとRxJava2の欠陥についてです。 つまり、nullは使用できません。 理由を理解してみましょう。 このJavaインターフェースを見てください。
public interface UserDataSource { Single<User> load(); }
結果がヌルになることはありますか? あいまいさを避けるため、RxJava2ではnullを使用できません。 まだ必要な場合は、多分オプションです。 しかし、コトリンではそのような問題はありません。
Single<User>
と
Single<User?>
異なるタイプであり、すべての問題がコンパイル段階で発生すると言うことができます。
共分散と反分散
これはKotlinの特徴的な機能であり、Javaには非常に欠けています。 詳細については、 マニュアルを参照してください 。 KotlinでRxJavaを使用する際にどのような問題が発生するかについて、興味深い例をいくつか紹介します。
共分散 :
fun bar(source: Observable<CharSequence>) { } fun foo(source: Observable<String>) { bar(source) // }
Observable
はJavaインターフェースであるため、このようなコードはコンパイルされません。 これは、Javaのジェネリック型が不変だからです。 もちろん使用することもできますが、スキャンなどの演算子を使用すると再びコンパイルエラーが発生します。
fun bar(source: Observable<out CharSequence>) { source.scan { a, b -> "$a,$b" } // } fun foo(source: Observable<String>) { bar(source) }
スキャン演算子は、ジェネリック型「T」が入力と出力の両方であるという点で異なります。 ObservableがKotlinインターフェイスである場合、そのタイプTはoutと示され、これにより問題が解決します。
interface Observable<out T> { … }
そして、これが反変の例です:
fun bar(consumer: Consumer<String>) { } fun foo(consumer: Consumer<CharSequence>) { bar(consumer) // }
前の例と同じ理由で(Javaのジェネリック型は不変です)、この例はコンパイルされません。 追加することで問題は解決しますが、やはり100パーセントではありません。
fun bar(consumer: Consumer<in String>) { if (consumer is Subject) { val value: String = consumer.value // } } fun foo(consumer: Consumer<CharSequence>) { bar(consumer) } interface Subject<T> : Consumer<T> { val value: T }
まあ、伝統により、Kotlinではこの問題はインターフェースでinを使用することで解決されます:
interface Consumer<in T> { fun accept(value: T) }
したがって、ジェネリック型のバリエーションと反則性は、Reaktiveライブラリの3番目の自然な利点です。
コトリン+リアクティブ= Reaktive
主なもの-Reaktiveライブラリの説明に移ります。
その機能のいくつかを次に示します。
- これはマルチプラットフォームです。つまり、最終的に一般的なコードを書くことができます。 Badooでは、これを最も重要な利点の1つと考えています。
- これはKotlinで書かれており、上記の利点があります。null、分散/反分散には制限がありません。 これにより、柔軟性が向上し、コンパイル中のセキュリティが確保されます。
- RxJava、RxSwiftなど、他のライブラリに依存することはありません。つまり、ライブラリの機能を共通の要素にする必要はありません。
- 純粋なAPI。 たとえば、Reaktiveの
ObservableSource
インターフェースは単にObservable
と呼ばれ、すべての演算子は別々のファイルにある拡張関数です。 15,000行の神クラスはありません。 これにより、既存のインターフェイスやクラスを変更することなく、機能を簡単に増やすことができます。
- スケジューラーのサポート(使い慣れた
subscribeOn
およびobserveOn
)。
- RakJava2との互換性(相互運用性)。ReaktiveとRxJava2の間のソース変換、およびRxJava2からスケジューラーを再利用する機能を提供します。
- ReactiveXコンプライアンス。
ライブラリがKotlinで書かれているという事実により得られた利点についてもう少しお話ししたいと思います。
- Reaktiveでは、Kotlinでは安全であるため、null値が許可されます。 以下に興味深い例を示します。
-
observableOf<String>(null) //
-
val o1: Observable<String?> = observableOf(null)
val o2: Observable<String> = o1 // ,
-
val o1: Observable<String?> = observableOf(null)
val o2: Observable<String> = o1.notNull() // , null
-
val o1: Observable<String> = observableOf("Hello")
val o2: Observable<String?> = o1 //
-
val o1: Observable<String?> = observableOf(null)
val o2: Observable<String> = observableOf("Hello")
val o3: Observable<String?> = merge(o1, o2) //
val o4: Observable<String> = merge(o1, o2) // ,
バリエーションも大きな利点です。 たとえば、Observable
インターフェイスでは、型Tがout
として宣言さout
ているため、次のような記述が可能になります。
fun foo() { val source: Observable<String> = observableOf("Hello") bar(source) // } fun bar(source: Observable<CharSequence>) { }
-
これは、今日のライブラリの外観です。
- 執筆時のステータス:アルファ(パブリックAPIの一部の変更が可能です);
- サポートされるプラットフォーム:JVMおよびAndroid。
- サポートされるソース:
Observable
、Maybe
、Single
およびCompletable
;
- map、filter、flatMap、concatMap、combinateLatest、zip、mergeなど、かなり多数の演算子がサポートされています(完全なリストはGitHubにあります )。
- 次のスケジューラーがサポートされています:計算、IO、トランポリン、メイン。
- サブジェクト:PublishSubjectおよびBehaviorSubject;
- バックプレッシャーはまだサポートされていませんが、この機能の必要性と実装について考えています。
近い将来の計画は何ですか:
- 製品でReaktiveの使用を開始します(現在、オプションを検討しています)。
- JavaScriptサポート(プルリクエストはすでにレビュー中);
- iOSサポート
- JCenterでの成果物の公開(現在はJitPackサービスを使用);
- ドキュメンテーション
- サポートされるオペレーターの数の増加。
- テスト
- より多くのプラットフォーム-プルリクエストは大歓迎です!
ライブラリを今すぐ試すことができ、必要なものはすべてGitHubで見つけることができます。 あなたの経験を共有し、質問をしてください。 フィードバックに感謝します。