Kotlinの面白い拡張関数

8373 ワード

Kotlin
Javaプラットフォーム上の新しいプログラミング言語をターゲットとしています.簡潔で、安全で、実用的で、Javaと相互運用できます.サービス開発、Androidアプリケーション開発、またはそれ以上のJavaで使用できるすべての場所に使用できます.Kotlinはすべての存在するJavaライブラリとフレームワークと一緒に良好に動作し、Javaのような効率を有する.
kotlinでは、関数はオブジェクトと同様に「一等公民」であり、これはkotlinでは、変数とデータ構造に格納したり、パラメータとして他の高次関数に渡したり、高次関数数の戻り値として使用したり、他の非関数値のように関数を呼び出したりすることができることを示す.
Kotlin拡張関数
Kotlinは、既存のクラスを継承したり、装飾モードを使用したりすることなく拡張することをサポートしています.拡張後、対応するクラスを通じて直接呼び出すことができます.関数でthisを使用してオブジェクトに直接アクセスできるメンバー変数
拡張関数の定義
Androidでは次のようにToast操作を完了しました.ほとんどの場合、utilsを定義することで呼び出しを容易にし、kotlinでは拡張関数の形式でtoast操作を定義することができます.
//     
inline fun Context.toast(msg: String) {
    Toast.makeText(this, msg, Toast.LENGTH_LONG).show()
}
inline fun Fragment.toast(msg: String) {
    Toast.makeText(activity, msg, Toast.LENGTH_LONG).show()
}
//   
toast("hello toast")

この背後には、kotlinが定義した拡張関数をツールクラスの形式に変換し、メソッドパラメータが拡張オブジェクトに渡されることもあります.ただ、使用上は開発者にとって透明です
標準ライブラリの拡張関数
Kotlinは開発者に多くの標準拡張関数を提供し、let、apply、also、runの4つの拡張関数の具体的な定義と使用を見てみましょう.
let拡張関数
メソッド定義
  • T汎用:ターゲット拡張オブジェクト
  • R汎用:関数パラメータの戻り値
  • block関数:インタフェースパラメータはTオブジェクト、戻り値はR
  • block関数を実行し、その結果
  • を返す.
    public inline fun  T.let(block: (T) -> R): R {
        return block(this)
    }
    

    使用例
            //        、    
            listView?.let {
                it.setFooterDividersEnabled(true)
                it.setSelectionAfterHeaderView()
                it.divider = null
                var adapter = ArrayAdapter(it.context, android.R.layout.simple_list_item_1)
                it.adapter = adapter
                adapter
            }?.let { 
                it.addAll(listOf("item1", "item1"))
            }
    

    apply拡張関数
    メソッド定義
  • T汎用:ターゲット拡張オブジェクト
  • block関数:Tの拡張関数であるため、関数内でthisまたはターゲットオブジェクトを直接参照するメンバー変数
  • を使用することができる.
  • block関数を実行し、ターゲットオブジェクト自体
  • に戻る.
    public inline fun  T.apply(block: T.() -> Unit): T {
        block()
        return this
    }
    

    使用例
            //      ,           ,     
            var paint = Paint().apply {
                isAntiAlias = true
                color = Color.WHITE
                xfermode = PorterDuffXfermode(PorterDuff.Mode.DST_IN)
            }
    

    Also拡張関数
    メソッド定義
  • T汎用:ターゲット拡張オブジェクト
  • block関数:インタフェースパラメータがTオブジェクト、戻り値が空の
  • block関数を実行し、ターゲットオブジェクト自体
  • に戻る.
    public inline fun  T.also(block: (T) -> Unit): T {
        block(this)
        return this
    }
    

    使用例
          stuInfo.also {
                it.append(stu?.name)
                it.append(stu?.age)
                it.append(stu?.code)
            }.toString()
    

    run拡張関数
    メソッド定義
  • T汎用:ターゲット拡張オブジェクト
  • R汎用:関数パラメータの戻り値
  • block関数:Tの拡張関数であるため、関数内でthisまたはターゲットオブジェクトを直接参照するメンバー変数
  • を使用することができる.
  • block関数を実行し、その結果
  • を返す.
    public inline fun  T.run(block: T.() -> R): R {
        return block()
    }
    

    使用例
            file?.run { 
                listFiles()
            }?.run { 
                forEach { 
                    it.delete()
                }
            }
    

    inlineキーワードについて
    Kotlinは生まれながらにして関数式のプログラミングをサポートしており、高次関数とlambdaはその大きな特色である高次関数を使用すると、実行時間の効率が損なわれます.実際にlambda式のコンパイルを呼び出すと、匿名の内部クラスが生成され、そのオブジェクトが作成され、その関数を呼び出して実装されます.しかし、inlineキーワードを使用すると、inline functionを呼び出すと、コンパイラは関数呼び出しを1回も生成せず、呼び出すたびにinline functionのコードを呼び出しに直接埋め込むことができます.
    let、apply、also、runまとめ
    上記の例から、これらの拡張関数は非常に似ていることがわかりますが、関数体におけるターゲットオブジェクトへの参照方法と関数の戻り値の2つの違いだけです.
    拡張関数
    参照先オブジェクト方式
    戻り値
    let
    it
    関数の戻り値
    apply
    this
    オブジェクト自体
    also
    it
    オブジェクト自体
    run
    this
    関数の戻り値
    Androidは拡張関数を使用
    開発の過程で,拡張関数を定義することによって,より効率的な符号化を提供することができる.Android KTXは、Android Jetpackシリーズに属するKotlin拡張プログラムのセットです.Kotlinが使用するJetpackとAndroidプラットフォームAPIを最適化しました.Android KTXは、拡張関数/プロパティ、lambda、命名パラメータ、パラメータのデフォルト値などのKotlin言語機能を使用して、Kotlinを使用してAndroidの開発をより簡潔で、より快適で、より慣用的な方法で行うことを目的としています.
    次に、Android KTXの例を見て、公式に定義されている拡張関数を見てみましょう.
    アニメーションリスニング
    拡張関数を使用しない場合は、アニメーションをリスニングします.
        val animator = ObjectAnimator.ofFloat(view, "translationX", 0f, 100f)
        animator.addListener {
            object : Animator.AnimatorListener {
                override fun onAnimationEnd(animation: Animator?) {
                }
    
                override fun onAnimationCancel(animation: Animator?) {
                }
    
                override fun onAnimationStart(animation: Animator?) {
                }
    
                override fun onAnimationRepeat(animation: Animator?) {
                }
    
            }
        }
    

    次にandroidx.core.animation.Animator.ktの拡張関数を使用します
    val animator = ObjectAnimator.ofFloat(view, "translationX", 0f, 100f)
        animator.addListener(onCancel = {
            // something
        })
    

    やはり簡潔で快適で、どのように実現したのでしょうか.Animator.ktのソースコードを見てみましょう
  • Animatorの拡張関数addListener
  • を定義する
  • onEnd、onStart、onCancel、onRepeatを受信し、デフォルト値がNull関数
  • であることを指定します.
  • 拡張関数でAnimatorListenerの匿名オブジェクトを作成し、関数パラメータを対応する関数
  • に割り当てる.
  • では、使用時に必要な関数を指定するだけで
  • になります.
    inline fun Animator.addListener(
        crossinline onEnd: (animator: Animator) -> Unit = {},
        crossinline onStart: (animator: Animator) -> Unit = {},
        crossinline onCancel: (animator: Animator) -> Unit = {},
        crossinline onRepeat: (animator: Animator) -> Unit = {}
    ): Animator.AnimatorListener {
        val listener = object : Animator.AnimatorListener {
            override fun onAnimationRepeat(animator: Animator) = onRepeat(animator)
            override fun onAnimationEnd(animator: Animator) = onEnd(animator)
            override fun onAnimationCancel(animator: Animator) = onCancel(animator)
            override fun onAnimationStart(animator: Animator) = onStart(animator)
        }
        addListener(listener)
        return listener
    }
    

    SharedPreferences
    通常はこのように使用されています
            val sp = getSharedPreferences("test", Context.MODE_PRIVATE)
            sp.edit().putString("name", "liu").commit()
            sp.edit().putInt("age", 20).commit()
            sp.edit().putBoolean("isMale", true).commit()
    

    拡張パラメータでは
            sp.edit {
                putString("name", "liu")
                putInt("age", 20)
                putBoolean("isMale", true)
            }
    

    拡張関数の定義は次のとおりです.
    inline fun SharedPreferences.edit(
        commit: Boolean = false,
        action: SharedPreferences.Editor.() -> Unit
    ) {
        val editor = edit()
        action(editor)
        if (commit) {
            editor.commit()
        } else {
            editor.apply()
        }
    }
    
    
  • SharedPreferencesのedit拡張関数
  • を定義する
  • commitパラメータ:commitがディスクファイル
  • にタイムリーにコミットされるかどうか
  • actionパラメータ:SharedPreferences.Editorの拡張関数であり、Editor
  • に入力する必要がある
    カスタム拡張関数
    //   EditText    ,    TextChange
    inline fun EditText.onTextChange(
            crossinline afterChanged: (s: Editable?) -> Unit = {},
            crossinline beforeChanged: (s: CharSequence?, start: Int, count: Int, after: Int) -> Unit = { s, start, couunt, after -> },
            crossinline onChanged: (s: CharSequence?, start: Int, before: Int, count: Int) -> Unit = { s, start, before, count -> }
    ) {
        val listener = object : TextWatcher {
            override fun afterTextChanged(s: Editable?) = afterChanged(s)
            override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) = beforeChanged(s, start, count, after)
            override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) = onChanged(s, start, before, count)
        }
        addTextChangedListener(listener)
    }
    

    拡張関数ではパラメータのデフォルト値を指定しますので、使用時に必要な関数を指定できます.例えばonTextChangedを監視するだけです
            editText.onTextChange(
                    onChanged = { c: CharSequence?, i: Int, i1: Int, i2: Int ->
                    }
            )
    

    面倒なので拡張関数を追加します
    inline fun EditText.onChanged(
            crossinline onChanged: (s: CharSequence?, start: Int, before: Int, count: Int) -> Unit = { _, _, _, _ -> }) {
        onTextChange(onChanged = onChanged)
    }
    

    使用する場合は、onChangedメソッドを単独で呼び出します.
            editText.onChanged { s, start, before, count ->
    
            }