Kotlinは高次関数を用いて反転方式を実現する。


lamdaと高次関数
以前はlambankと高次関数を勉強しました。そしてAndroid開発においてonClickイベントをモニターするのはよく使われている機能です。

  rootView.setOnClickListener { view ->
    println("     ID=${view.id} view")
  }
そして、開発において避けられないコードを作成します。この時もjavaの思想で実現すれば、ちょっと遠いです。
java思想の実現
javaでは私たちの一般的なやり方はこうです。
インターフェースを定義する
インターフェースタイプ変数を定義します。
セット方法を定義します。
setメソッドを呼び出してインターフェースの実現クラスを設定します。
kotlinで実現すると以下の通りです。

class MyView{
  //      
  interface IOnLabelCheckedListener {
    fun onLabelCheck(label: String)
  }
  //          
  private var onLabelChecked: IOnLabelCheckedListener? = null

  private fun initView(context: Context) {
    view.setOnCheckedChangeListener { radioGroup, i ->
        onLabelChecked.onLabelCheck(radioGroup.findViewById<RadioButton>(i).text.toString())
    }
  }
  //     set   
  fun setOnLabelCheckedListener(e: IOnLabelCheckedListener) {
    this.onLabelChecked = e
  }
}

   //   set  ,         
    MyView.setOnLabelCheckedListener(object : LabelBarView.IOnLabelCheckedListener {
      override fun onLabelCheck(label: String) {

      }
    })
このように実現された問題
もちろん複雑すぎます。しかも最初はこう書いていましたが、なぜMyView.setOnLabel CheckedListenerメソッドの内部にはlamband表現が入ってこないのか分かりません。lamda表現の存在は匿名の内部カテゴリに取って代わるためではないですか?また、このインターフェースがJavaタイプのインターフェースである場合には、lambada式を使用することができます。なぜですか?最後の予想は、コードリンがjavaと通話している間にもう一つの層を包んでいるからです。直接にコードリンを使って、このインターフェースは中間層がないと定義します。私たちが定義したset方法は高次関数ではないので、もちろんlamband表現を使うことはできません。
ここでは、kotlinの思想を使ってリプライを行います。
高次関数を用いて実現した。
kotlinとjavaは関数プログラミングという重要な違いがあります。関数式プログラミングの思想の中で関数は1等の公民で、kotlinを使う時私達は多くこのような思惟を利用して問題を考えます。Kotlinでは高次関数を提供しています。直接に戻り値として関数を使用できます。Javaプログラミングに慣れている私にとっては、最初は理解するのが難しいです。次は高次関数を実現するためのステップを書いてみます。みなさんに助けてほしいです。
まず、考えられるのは関数伝達です。匿名の内部カテゴリをドロップする代わりに、lamberを使ってこのように実現できます。

//        ,         lambda   
MyView.setOnLabelCheckedListener(object : MyView.IOnLabelCheckedListener {
    override fun onLabelCheck(label: String) {
     println(label)
    }
})
//    MyView.IOnLabelCheckedListener         onLabelCheck(label: String)
//        lambda      
var lam: (String) -> Unit = { label -> println(label) }
そして、作成したlamdaを送る必要があります。この時にset OnLabel CheckedListener方法を要求するのは高次関数です。

  //                    lam ,          e            
  fun setOnLabelCheckedListener(e: (String) -> Unit) {
    this.lisenter = e
  }
 
  //   lisenter        
  var linsnter: (String) -> Unit = {}
最後にlinsnterを使ってリマインドを行います。

  private fun initView(context: Context) {
    view.setOnCheckedChangeListener { radioGroup, i ->
      linsnter(radioGroup.findViewById<RadioButton>(i).text.toString())
    }
  }
最終コードの結果:

class MyView{
  var linsnter: (String) -> Unit = {}

 private fun initView(context: Context) {
    view.setOnCheckedChangeListener { radioGroup, i ->
      linsnter(radioGroup.findViewById<RadioButton>(i).text.toString())
    }
 }

 fun setOnLabelCheckedListener(e: (String) -> Unit) { 
  this.lisenter = e
 }
}
  //        lam   ,         
  view.setOnLabelCheckedListener { label ->
    println(label)
  }
最後のコードと前のコードは最大の違いがあります。一つはインターフェースの定義がないこと、もう一つは匿名の内部クラスがないことです。
高次関数をより良く使う
高次関数の使用は、下記のコードのように、コードをより簡潔にすることができます。

  fun refreshData(e: ((Boolean, String) -> Unit)): Boolean {

    if (!UserInfoManager.getInstance().isLogin) {
      e(false, "   ")
      return false
    }

    NETWorkUtils.request(ApiParamter(), object : ApiListener<ResponseData> {
      override fun onApiCompleted(data: ResponseData?) {
          e(true, "  ")
      }

      override fun onApiError(errorCode: Int, errorCodeMessage: String) {
         e(false, errorCodeMessage)
      }
    })
    return true
  }
それを呼び出すときは、このようにしてもいいです。

   mView.refreshData { isSuccess, msg ->
      //do something
  }
簡単ですか?省略してもう一つのインターフェースを書きます。また、javaでrefreshDataを呼び出す方法であれば同じです。

    mView.refreshData(new Function2<Boolean, String, Unit>() {
      @Override
      public Unit invoke(Boolean aBoolean, String s) {
        // do something
        return null;
      }
    });
Kotlinは、Javaの高次関数の呼び出しに使用するための一連のFunctionインターフェースクラスを提供しており、最大22つのパラメータをサポートしています。
以上はKotlinにおいて高次関数を用いて伝統的なコールバック関数を置換する方法である。違うところはまた教えてください。参考にしていただければと思いますが、どうぞよろしくお願いします。