Как Kotlin распознает лямбда-приемник в функции «использования» - PullRequest
0 голосов
/ 02 октября 2019

Когда я смотрю на пример кода для функции «use» в Kotlin, я обычно вижу что-то вроде этого:

private fun readFirstLine(): String {
    BufferedReader(FileReader("test.file")).use { return it.readLine() }
}

Однако, в следующем примере я не понимаю, где «input»происходит из-за того, что input -> является лямбда-выражением. Насколько я понимаю, все внутри использования {} должно быть выражением:

val streamIn = resources.openRawResource(rawResId)
val streamOut = FileOutputStream(destFilename)

streamIn.use { input ->
    streamOut.use { output ->
        input.copyTo(output)
    }
}

«вход» явно относится к тому же объекту, на который ссылается «streamIn», но я не понимаю, откуда Котлин знает, что.

Ответы [ 3 ]

2 голосов
/ 02 октября 2019

все внутри использования {} должно быть выражением

Если вы посмотрите на подпись , вы увидите, что use занимает (T) -> R функция, так что любая функция / лямбда, которая принимает закрываемый объект в качестве параметра, может быть передана ему.

После того, как это заблуждение прояснится, давайте посмотрим, что делает рассматриваемый код.

streamIn.use { input ->
    streamOut.use { output ->
        input.copyTo(output)
    }
}

Сначала мы видим streamIn.use {, что означает, что мы собираемся сделать что-то с streamIn, а затем закроем это. И с этого момента streamIn будет называться input. Затем есть streamOut.use {, что означает, что мы также будем использовать streamOut для выполнения каких-либо задач, а затем закроем его, и с этого момента мы будем называть его output.

Я не понимаю, откуда взято "input"

Это в основном дает другое имя it, как в вашем первом фрагменте кода. Так как здесь мы вложили лямбды, мы не можем использовать it для ссылки на параметры обеих лямбд.

«вход» явно относится к тому же объекту, на который ссылается «streamIn», ноЯ не понимаю, как Котлин это знает.

Это потому, что в реализации use, вероятно, есть такая строка:

return block(this)

block isлямбда-параметр, который вы передаете use, а this - это объект, для которого вызывается use. Поскольку input является параметром лямбды, он относится к this.

Теперь мы объявили, что будем использовать два ресурса, что с ними делать? input.copyTo(output)! Все, что возвращает copyTo, будет возвращено streamOut.use, которое, в свою очередь, будет возвращено streamIn.use. streamOut и streamIn также будут закрываться один за другим.

Итак, что мы сделали в целом? Мы в основном использовали 2 ресурса одновременно и закрыли их впоследствии. Вот как вы составите use, чтобы использовать несколько ресурсов одновременно.

1 голос
/ 02 октября 2019

в лямбда-выражении вы можете определить имя для своего объекта, поэтому в следующем коде input эквивалентно it, что равно streamIn, а вывод равен streamOut:

streamIn.use { input ->
    streamOut.use { output ->
        input.copyTo(output)
    }
}

Причина, по которой они определяют input и output, заключается в том, что вы не можете использовать его, когда используете лямбда-блок внутри другого лямбда-блока.

0 голосов
/ 02 октября 2019

use - это функция расширения , которая принимает в качестве параметра все, что вызывает ее.

Предположим, этот пример:

file.bufferedReader().use{
   println(it.readText()) // it is actually that object that calls `use`
}

В соответствии с API-документы от Kotlin, это схема use:

inline fun <T : AutoCloseable?, R> T.use(block: (T) -> R): R

В моем примере bufferedReader является закрываемым классом.

Когда вы пишете somethingClosable.use { }вы фактически передаете ему лямбда-функцию, например:

fun <T, R> function(t: T): R {
   // use T and return an R
}

somethingClosable.use(function)

А внутри use будет вызываться ваша функция.

Дополнительная информация о расширениифункции в Котлине.

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