Kuにおけるスコープ関数


Kotlinは、オブジェクトの文脈の中からコードのブロックを実行するのを許しますscope functions . 小林は5人を定義する.let , run , with , apply and also . 残念なことに、DARTプログラミング言語の制限のために、それらのうちの3つだけが実行されることができます:let , run (非拡張メソッドとして)also .
私たちは、実装、可能なユースケース、および実際のライブラリについては後で話します.第1に、我々はコリンが輝く場所とダーツが失敗するところを調査する必要があります.

コルト要塞対ダート制限


からofficial documentation , 高次関数は、1つまたは複数の関数をパラメーターとして受け取り、関数を返す関数です.kotlinの高次関数は,関数リテラルのアイデアを受信機でサポートする.例えば、型A.(B) -> C 受信オブジェクトで呼び出すことができる任意の関数を表しますA 型のパラメータB , 返り値C .
この動作は、拡張機能内の受信オブジェクトのメンバーにアクセスできるように拡張機能に似ています.
さあ、見ましょうapply , スコープ関数の一つ:
inline fun <T> T.apply(block: T.() -> Unit): T {
  block()
  return this
}
こちらです.block パラメータはレシーバとの関数リテラルです.コードを読んで、kotlinの創意工夫を観察しましょうT , パラメータを受け入れず、何も返さない任意のメソッドを定義することができますT .
実際には、このように使用することができます.
data class User(
  val username: String,
  var email: String = "",
  var age: Int = -1
)

fun main() {
  val user = User(username = "john_doe")
  user.apply {
    email = "[email protected]"
    age = 25
  }
}
この例では、メールオブジェクトと年齢割り当てをユーザーオブジェクトに適用します.なしでapply , 手動で割り当てを処理する必要があります
val user = User(username = "john_doe")
user.email = "[email protected]"
user.age = 25

ダートの話


フードの下で、拡張機能は静的に解決されます.そして、それらが拡張しているクラスとの接続を持たない通常の静的メソッドであるということです.コンパイラは拡張機能を生成します.
ダートはこの動作を提供しません.
それで、あなたは尋ねるかもしれません、プロトタイプまたはプロトタイプの継承で働くことは可能ですか?このようにして、クラスのシグネチャを変更し、JavaScript開発者と同じようなものに頼りますFunction.prototype.call ?
答えは、もう一度、負です.ダートはこの動作を提供しません.プロトタイプ/プロトタイプチェーンではなく、継承に依存します.
これらの2つの制限が我々に反対して、残りだけが反射です.DARTのサポートを介して反射dart:mirrors パッケージ(標準ライブラリの一部)、しかし、私は上記の能力を達成することができるAPIを生産することができませんでした.

スコープ関数の実装


方法の制限についての議論で、最初に実装を議論しましょう.run (非拡張機能として)実装するのが最も簡単です.それは単に戻り型を持ついくつかの文の「ラッパー」ブロックです.
小林でrun is
inline fun <R> run(block: () -> R): R = block()
ダーツでも同様に
R run<R>(R Function() block) => block();
let コリンでは
inline fun <T, R> T.let(block: (T) -> R): R = block(this)
DARTでは、拡張メソッド構文を使用する必要があります
extension ScopeFunctionLet<T extends Object> on T {
  R let<R>(R Function(T self) block) => block(this);
}
最後にalso コリンでは
inline fun <T> T.also(block: (T) -> Unit): T {
  block(this)
  return this
}
ダーツで
extension ScopeFunctionAlso<T extends Object> on T {
  T also(void Function(T self) block) {
    block(this);
    return this;
  }
}

ユースケース


使用例


つの異なる米国のケースがありますlet :
  • コールチェーンの結果に1つ以上の関数を呼び出す
  • 非NULL値だけでコードブロックを実行する
  • コードの読みやすさを改善するための限られた範囲でローカル変数を導入する
  • void main1() {
      // First use case
      ["one", "one", "two", "three", "four", "five"]
          .map((String it) => it.length)
          .where((int it) => it > 3)
          .let((Iterable<int> it) => print('Number of results: ${it.length}'));
    
      // Second use case
      'Hey there!'?.let((it) => it.length);
    
      // Third use case
      ["one", "one", "two", "three", "four", "five"]
          .first
          .let((it) => it.length < 5 ? '!' : it);
    }
    
    void main2() {
      // First use case without "let"
      final numbers =   ["one", "one", "two", "three", "four", "five"]
          .map((String it) => it.length)
          .where((int it) => it > 3);
      print('Number of results: ${numbers.length}');
    
      // Second use case without let
      final String? nullableString = 'Hey there!';
      if (nullableString != null) {
        nullableString.length;
      }
    
      // Third use case without let
      var firstElement = ["one", "one", "two", "three", "four", "five"]
          .first;
      if (firstElement.length < 5) {
        firstElement = '!';
      }
    }
    

    使用事例


    非拡大run 式が必要ないくつかの文のブロックを実行します.
    void main3() {
      final hexNumberRegex = run(() {
        final digits = '0-9';
        final hexDigits = 'a-fA-F';
    
        return RegExp('[$digits$hexDigits]+');
      });
    
      final matches = hexNumberRegex
          .allMatches("+123 -FFFF !%*& 88 XYZ")
          .map((RegExpMatch match) => match.group(0));
      print(matches); // '123', 'FFFF', '88'
    }
    

    使用例

    also and apply 技術的に同じですが、違いは文脈オブジェクトであるということですalso レシーバの代わりに引数として利用できます.コンテキストオブジェクトを引数として扱うアクションを実行するときに使用されます.これは、アクションがプロパティ/メソッドではなくオブジェクトへの参照を必要とする場合です.
    [3, 1, 5].also((it) => it.sort()).add(6);
    

    ソース


    ライブラリへのソースコードはGithubアカウントで利用可能です.現時点では利用できませんpub.dev , むしろGitの依存関係です.