Can I send extension function through function parameter
Asked Answered
G

2

7

I have some extension functions function below.

fun EditText.setEmailValidationListener(): TextWatcher {
    val textWatcher = object : TextWatcher {
        override fun beforeTextChanged(text: CharSequence?, start: Int, count: Int, after: Int) { }
        override fun onTextChanged(text: CharSequence?, start: Int, before: Int, count: Int) { }
        override fun afterTextChanged(text: Editable?) { validateEmail() }

        private fun validateEmail(): Boolean {
            if (validateEmailFormat(showError = false)) {
                getParentInputLayout()?.isErrorEnabled = false
                return true
            }
            return false
        }
    }
    addTextChangedListener(textWatcher)

    return textWatcher

}

fun EditText.setPasswordValidationListener(): TextWatcher {
    val textWatcher = object : TextWatcher {
        override fun beforeTextChanged(text: CharSequence?, start: Int, count: Int, after: Int) { }
        override fun onTextChanged(text: CharSequence?, start: Int, before: Int, count: Int) { }
        override fun afterTextChanged(text: Editable?) { validateEmpty() }

        private fun validatePasswordText(): Boolean {
            if (validateEmptyText(showError = false)) {
                getParentInputLayout()?.isErrorEnabled = false
                return true
            }
            return false
        }
    }

    addTextChangedListener(textWatcher)

    return textWatcher
}

fun EditText.validateEmailFormat(showError: Boolean = true): Boolean 
{
    // Do something checking the Email
    return false
}

fun EditText.validatePasswordText(showError: Boolean = true): Boolean     
{
    // Do something checking the Password
    return false
}

private fun EditText.getParentInputLayout(): TextInputLayout? {
    if (parent is TextInputLayout) {
        return parent as TextInputLayout
    }
    return null
}

Both setEmailValidationListener and setPasswordValidationListener are identical, except for the validation function they use respectively i.e. validateEmailFormat and validatePasswordFormat.

So I plan to refactor the two function common code into a common function as below

fun EditText.setupTextChangeListener(validatorFunc : (showError: Boolean) -> Boolean): TextWatcher {
    val textWatcher = object : TextWatcher {
        override fun beforeTextChanged(text: CharSequence?, start: Int, count: Int, after: Int) { }
        override fun onTextChanged(text: CharSequence?, start: Int, before: Int, count: Int) { }
        override fun afterTextChanged(text: Editable?) { validateEmpty() }

        private fun validateEmpty(): Boolean {
            if (validatorFunc(false)) {
                getParentInputLayout()?.isErrorEnabled = false
                return true
            }
            return false
        }
    }

    addTextChangedListener(textWatcher)

    return textWatcher
}

... where it basically just to send in validationFunc as parameter to it.

However, I can't find any way of sending the EditText.validateEmailFormat and EditText.validatePasswordFormat into the validationFunc function parameter.

How could I achieve that?

Gwin answered 19/8, 2016 at 2:32 Comment(0)
B
9

Some theory

Signature of extension functions is bit more complicated than in may look at first. The extension needs to have some reference to object of this class to be able to act upon it.

In fact the extension method

fun EditText.validateEmailFormat(showError: Boolean = true): Boolean

after decompiling to plain old java, looks like this:

public static final boolean validateEmailFormat(@NotNull EditText $receiver, boolean showError)

As it's (almost) impossible to change already-compiled Java class. So Kotlin (and quite possibly other languages that have concept of extension methods) uses static methods, with first parameter being receiver of extending class, to make it work.

Back to the business

Your validateEmailFormat is in fact of type EditText.(Boolean) -> Boolean and at the same time is of type (EditText, Boolean) -> Boolean. So you need to do either of two things:

First you can make EditText.setupTextChangeListener accept validatorFunc as EditText.(Boolean) -> Boolean or (EditText, Boolean) -> Boolean instead of (Boolean) -> Boolean.

Or you refrain from extending EditText in fun EditText.validateEmailFormat(Boolean) and make it plain Kotlin function, e.g. something like this fun validateEmailFormat(String, Boolean).

As you are extensively using extension functions, I assume the first option is correct solution for you.

Bothy answered 19/8, 2016 at 6:20 Comment(2)
In addition to the explanation above I suggest adding an example using validatorFunc: EditText.(showError: Boolean) -> Boolean. e.g.: editText.setupTextChangeListener(EditText::validateEmailFormat)Pylos
Love it absolutely!Gwin
S
4

fun EditText.validateEmailFormat() can be passed as EditText::validateEmailFormat.

Spirochaetosis answered 3/3, 2022 at 10:27 Comment(1)
This was enormous help to me. I'm ashamed it was so obvious and simple. >_<Lorrainelorrayne

© 2022 - 2024 — McMap. All rights reserved.