[解決済み] Kotlinの "receiver "って何?
質問
拡張機能とどのような関係があるのですか?なぜ
with
関数なのか
キーワードではないのですか?
このトピックに関する明示的な文書はないようです。 拡張機能 .
どのように解決するのですか?
確かに、レシーバの概念に関する既存のドキュメントはほとんどないようです (唯一の例外は 拡張関数に関連する小さなサイドノート を参照)、これは驚くべきことです。
- から生まれた存在であり 拡張機能 ;
- における役割 DSL を構築する を構築する役割を担います。
-
標準ライブラリの存在
関数
with
のように見えるかもしれませんが、レシーバの知識がなければ キーワード ; - 完全に 関数型のための独立した構文 .
これらのトピックにはすべてドキュメントがありますが、レシーバーについて深く掘り下げたものはありません。
最初に
レシーバーって何?
Kotlin のコードのブロックは、型(あるいは複数の型)を レシーバ として持つことができ、修飾することなくそのブロックのコードでレシーバの関数とプロパティを利用できるようになります。
このようなコードのブロックを想像してみてください。
{ toLong() }
あまり意味はないですよね?実際、これを
関数型
の
(Int) -> Long
- ここで
Int
は(唯一の)パラメータで、戻り値の型は
Long
- である場合、当然ながらコンパイルエラーになります。これを解決するには、関数呼び出しを暗黙の単一パラメータである
it
. しかし、DSL構築の場合、これでは問題が山積みです。
-
DSL のネストされたブロックは、その上位レイヤーがシャドウされます。
html { it.body { // how to access extensions of html here? } ... }
これは、HTML DSL としては問題を起こさないかもしれませんが、他のユースケースとしては問題を起こすかもしれません。 -
コードに
it
の呼び出しで、特にパラメータ(もうすぐレシーバになる)を多用するラムダでは、コードを散らかしたりします。
これは レシーバー が効いてきます。
このコードブロックは、関数型に割り当てられることで
Int
として
レシーバ
(パラメータとしてではなく!) として指定すると、コードは突然コンパイルされます。
val intToLong: Int.() -> Long = { toLong() }
どうしたんだ?
ちょっとした余談
このトピックでは 関数の種類 を理解していることを前提としていますが、受信者のために少し補足が必要です。
関数型はまた 一つ レシーバを持つこともできます。その場合は、型の前にドットを付けます。例
Int.() -> Long // taking an integer as receiver producing a long
String.(Long) -> String // taking a string as receiver and long as parameter producing a string
GUI.() -> Unit // taking an GUI and producing nothing
このような関数型は、パラメータリストの先頭に受信機型が付きます。
レシーバーによるコードの解決
Receiver を持つコードのブロックがどのように処理されるかを理解するのは、実はとても簡単です。
拡張関数と同様に、コードのブロックが受信機型のクラス内部で評価されると想像してください。 これ は事実上、受信機タイプによって修正されるようになります。
先ほどの例では
val intToLong: Int.() -> Long = { toLong() }
の中の関数に置かれたように、コードのブロックが異なる文脈で評価されることになります。
Int
. このことをよりよく示すために、手作りの型を使用した別の例を示します。
class Bar
class Foo {
fun transformToBar(): Bar = TODO()
}
val myBlockOfCodeWithReceiverFoo: (Foo).() -> Bar = { transformToBar() }
は事実上、(コード上ではなく、心の中で - JVM上で実際にクラスを拡張することはできません)。
class Bar
class Foo {
fun transformToBar(): Bar = TODO()
fun myBlockOfCode(): Bar { return transformToBar() }
}
val myBlockOfCodeWithReceiverFoo: (Foo) -> Bar = { it.myBlockOfCode() }
クラスの内部で、どのように
this
にアクセスするために
transformToBar
- は、レシーバを持つブロックでも同じことが起こります。
たまたま、ドキュメントにある この のドキュメントでは、現在のコードブロックに 2 つのレシーバがある場合に、一番外側のレシーバを使用する方法も説明されており、それは 修飾された this .
待てよ、複数の受信機があるのか?
そうです。1つのコードブロックに 複数 レシーバーがありますが、これは現在のところ型システムで表現されていません。これを実現する唯一の方法は、複数の 高次関数 を使うことです。例
class Foo
class Bar
fun Foo.functionInFoo(): Unit = TODO()
fun Bar.functionInBar(): Unit = TODO()
inline fun higherOrderFunctionTakingFoo(body: (Foo).() -> Unit) = body(Foo())
inline fun higherOrderFunctionTakingBar(body: (Bar).() -> Unit) = body(Bar())
fun example() {
higherOrderFunctionTakingFoo {
higherOrderFunctionTakingBar {
functionInFoo()
functionInBar()
}
}
}
もしKotlin言語のこの機能があなたのDSLに不適切だと思われる場合は、注意してください。 DslMarker はあなたの味方です!
結論
なぜ、このようなことが重要なのでしょうか?この知識で
-
と書くことができる理由を理解しました。
toLong()
を書くことができるのはなぜか、ご理解いただけたと思います。 拡張関数は拡張であるべきではないのでは? - あなたの好きなマークアップ言語のための DSL を構築することができます。たぶん、1つまたは他の( 正規表現が必要なのは誰ですか? !).
-
あなたは、なぜ
with
は、標準ライブラリ 関数 は存在します。冗長なタイピングを節約するためにコードブロックのスコープを修正する行為は非常に一般的なので、言語設計者はそれを標準ライブラリに正しく配置しました。 - (たぶん) あなたはオフショットで関数型について少し学びました。
関連
-
[解決済み] Kotlin 三項条件演算子
-
[解決済み] by lazy" と "lateinit" を使ったプロパティの初期化
-
[解決済み] Kotlinのvarとvalの違いは何ですか?
-
[解決済み】KotlinでJavaの静的メソッドに相当するものは何ですか?
-
[解決済み】Kotlinでデータクラスを拡張する
-
[解決済み】KotlinフラグメントのviewにアクセスしようとするとNullPointerExceptionが発生する
-
[解決済み】JavaからKotlinの拡張関数にアクセスする
-
[解決済み】Kotlinの標準ライブラリで利用できるJava 8 Stream.collectに相当するものは?
-
[解決済み] Kotlinで抽象クラスのインスタンスを生成する
-
[解決済み] KotlinのIterableとSequenceは全く同じに見える。なぜ2つの型が必要なのでしょうか?
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
[解決済み] XHTMLの自己完結型タグを除くオープンタグにマッチするRegEx
-
[解決済み] Kotlinにコンストラクタ参照はありますか?
-
[解決済み] Kotlinで多くの例外を同時にキャッチするには?
-
[解決済み] Safeargsライブラリがディレクションクラスを生成しない
-
[解決済み] Kotlin データクラスのオーバーライドゲッター
-
[解決済み] Kotlinで抽象クラスのインスタンスを生成する
-
[解決済み] KotlinのIntArrayとArray<Int>の比較
-
[解決済み] KotlinのIterableとSequenceは全く同じに見える。なぜ2つの型が必要なのでしょうか?
-
[解決済み] KotlinのコルーチンはRxKotlinよりどう優れているか?
-
[解決済み] Kotlinにおけるスレッドとコルーチンの違い