Однофункциональные слушатели, использующие лямбду - PullRequest
1 голос
/ 28 июня 2019

Со всеми известными однофункциональными слушателями мы можем использовать более простую лямбда-нотацию

view.setOnClickListener { do() }

вместо оригинального, более длинного Java-способа

view.setOnClickListener(object : View.OnClickListener {
  override fun onClick(v: View?) {
    do()
  }
})

Но что именноделает эту работу?Я пытался сделать то же самое с моим собственным слушателем:

private var listener: OnCopyPasteClickListener? = null

interface OnCopyPasteClickListener {
  fun onPasteClick(text: String)
}

fun setOnCopyPasteClickListener(onCopyPasteClickListener: OnCopyPasteClickListener) {
  listener = onCopyPasteClickListener
}

, и хотя длинный подход работает просто отлично:

copypaste.setOnCopyPasteClickListener(object : CopyPasteMenu.OnCopyPasteClickListener {
  override fun onPasteClick(text: String) {
    do(text)
  }
})

Я не могу заставить его принять короткий:

copypaste.setOnCopyPasteClickListener {
  do(it)
}

В среде IDE выдается ошибка несоответствия типов.

Ответы [ 3 ]

1 голос
/ 28 июня 2019

На самом деле, если у вас есть только одна функция для вызова, я рекомендую вам использовать Kotlin Callback.

typealias OnDoWorkListener = ((String) -> Unit)

class Work {
    var doWork: OnDoWorkListener? = null

    fun doSomething() {
       doWork?.invoke("Message Here")
    }
}

И в вашей функции вы просто устанавливаете обратный вызов на нее

fun main() {
   val work = Work()

   work.doWork = {
       Log.d("WORK", "This gets called from the `work` object. Message: $it")
   }

   work.doSomething();
}

Мы также можем использовать функцию для настройки слушателя.

class Work {
    var doWork: OnDoWorkListener? = null

    fun doSomething() {
       doWork?.invoke("Message Here")
    }

    fun setOnWorkListener(listener: OnDoWorkListener) {
       doWork = listener
    }
}


fun main() {
   val work = Work()
   work.setOnWorkListener { 
       Log.d("WORK", "This gets called from the `work` object. Message: $it")
   }
   work.doSomething()
}

1 голос
/ 28 июня 2019

Функции высшего порядка делают эту работу:

Функции Kotlin являются первоклассными, что означает, что они могут быть сохранены в переменных и структурах данных передается в качестве аргументов и возвращается от других функций высшего порядка. Вы можете работать с функциями в любым способом, который возможен для других нефункциональных значений.

С на той же странице :

Передача лямбды на последний параметр

В Котлине существует соглашение, что если последний параметр функция принимает функцию, лямбда-выражение, которое передается как Соответствующий аргумент может быть заключен в скобки:

val product = items.fold(1) { acc, e -> acc * e }

Если лямбда является единственным аргументом этого вызова, скобки могут быть опущенным полностью:

run { println("...") }

Зная это, возможное обновление вашего класса будет выглядеть так:

class CopyPaste {
    private var listener: (String) -> Unit = {}

    fun setOnCopyPasteClickListener(onCopyPasteClickListener: (String) -> Unit) {
        listener = onCopyPasteClickListener
    }

    fun doCopyPaste(value: String) {
        listener.invoke(value)
    }
}

fun main() {
    val copyPaste = CopyPaste()
    copyPaste.setOnCopyPasteClickListener { println(it) }
    copyPaste.doCopyPaste("ClipboardContent!")
}

Класс CopyPaste хранит слушателя, который является функцией, которая принимает параметр String и ничего не возвращает. Его функция setOnCopyPasteClickListener принимает функцию с той же сигнатурой, что и у свойства listener, а в конце doCopyPaste принимает параметр String и передает его в сохраненную функцию.

0 голосов
/ 28 июня 2019

На самом деле, только после того, как я отправил, я искал больше мыслей и нашел эту тему: https://youtrack.jetbrains.com/issue/KT-7770 Это действительно обсуждается ограничение, как это в настоящее время применяется только к Java, не Котлин себя.Там также есть предложение, которое дает почти требуемую простоту:

interface OnCopyPasteClickListener {
  fun onPasteClick(text: String)

  companion object {
    inline operator fun invoke(crossinline op: (text: String) -> Unit) =
      object : OnCopyPasteClickListener {
        override fun onPasteClick(text: String) = op(text)
      }
  }
}

, а затем, благодаря этому перегруженному оператору, его можно назвать:

copypaste.setOnCopyPasteClickListener(CopyPasteMenu.OnCopyPasteClickListener { text ->
  do(text)
})

Но как предложеноответы предлагают более идиоматическое решение, я приму одно из них, я только хотел включить этот подход сюда для справки.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...