Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can I send extension function through function parameter

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?

like image 390
Elye Avatar asked Aug 19 '16 02:08

Elye


People also ask

What is true about extension functions in Kotlin?

Extension functions are a cool Kotlin feature that help you develop Android apps. They provide the ability to add new functionality to classes without having to inherit from them or to use design patterns like Decorator.

What is the receiver in the extension function?

Inside such an extension, there are multiple implicit receivers - objects whose members can be accessed without a qualifier. An instance of a class in which the extension is declared is called a dispatch receiver, and an instance of the receiver type of the extension method is called an extension receiver.

How Kotlin extension function works?

In particular, Kotlin extensions let you add functions to a class that you cannot modify. By using them, you will be able to call these new functions as if they were part of the original class. Similarly, you can use this mechanism to add new properties to existing classes. You can also extend Kotlin companion objects.


2 Answers

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.

like image 121
rafal Avatar answered Sep 23 '22 14:09

rafal


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

like image 30
Miloš Černilovský Avatar answered Sep 24 '22 14:09

Miloš Černilovský