Kotlin: В чем разница между Apply and Also
В чем разница между применением, а также. Из того, что я знаю, следующий код делает то же самое:
Применить
val person = Person().apply {
name = "Tony Stark"
age = 52
// More such stuff
}
также
val person = Person().also {
it.name = "Tony Stark"
it.age = 52
// More such stuff
}
Есть ли разница, и я должен использовать один над другим? Кроме того, есть ли случаи, когда можно было бы работать, а другой - нет?
Ответы
Ответ 1
TL; DR разница
also
функция получает лямбда, к которому T
передается в реализации, таким образом, внутри лямбда вы обратитесь к нему с именем (it
по умолчанию, можно переименовать { otherName ->...}
).
val person = Person().also {
it.name = "Tony Stark"
}
В apply
, с другой стороны, используется литерал функции с получателем, поэтому внутри переданной лямбды вам не нужно добавлять дополнительные префиксы для доступа к ее членам, как вы увидите в следующем. Получатель может ссылаться на this
.
val person = Person().apply {
name = "Tony Stark"
}
также
Декларация:
inline fun <T> T.also(block: (T) -> Unit): T (source)
Вызывает указанный функциональный блок с this
(получателя) в качестве аргумента и возвращает this
(получателя).
применять
Декларация:
inline fun <T> T.apply(block: T.() -> Unit): T (source)
Вызывает указанный функциональный блок с this
значением в качестве получателя и возвращает this
(получателя).
когда использовать что
Примеры использования объясняются в этой теме.
Ответ 2
Короткий ответ: also
был введен по смысловым причинам.
Длинный ответ:
Если вы используете apply
, вы всегда ссылаетесь на ресивер с помощью this
.
val person = Person().apply {
name = "Tony Stark" // this. can be omitted
age = 52 // this. can be omitted
// ...
}
Таким образом, вам не нужно повторять несколько раз, как показано здесь:
person.name = "Tony Stark"
person.age = 52
Если блок станет длиннее, вы можете указать this
имя. Вот почему also
был введен. Теперь вы можете обратиться к получателю либо с помощью it
, либо с явным именем. Это особенно полезно, если вы хотите использовать другое имя, чем (в данном случае person
) до:
val person = Person().also { newPerson ->
newPerson.name = "Tony Stark"
newPerson.age = 52
// ...
}
Итак, в зависимости от того, насколько хорошо вы должны читать код, вы всегда можете использовать тот или иной.
Ответ 3
Ответы, приведенные выше, имеют мало смысла, но не много. Я не понимаю это правильно, но я хотел бы добавить к вопросу здесь.
В Standard.kt это фактическая реализация для двух методов.
Для применения
/**
* Calls the specified function [block] with `this` value as its receiver and returns `this` value.
*/
@kotlin.internal.InlineOnly
public inline fun <T> T.apply(block: T.() -> Unit): T {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
block()
return this
}
Кроме того,
/**
* Calls the specified function [block] with `this` value as its argument and returns `this` value.
*/
@kotlin.internal.InlineOnly
@SinceKotlin("1.1")
public inline fun <T> T.also(block: (T) -> Unit): T {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
block(this)
return this
}
Два метода почти одинаковы, кроме одной строки. Только после объяснений я увидел разницу. Функциональный язык, такой как Kotlin, действительно сложный для Java-единомышленника, такого как я.