Должны ли стандартные функции Kotlin (запускать, позволять, также применять) использоваться для развёртывания опций, или для этого должен быть отдельный метод? - PullRequest
1 голос
/ 25 апреля 2019

Итак, я читал о стандартных функциях run, let, also, apply, поскольку видел, как они используются в разных случаях в моей кодовой базе, и обнаружил две вещи:

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

Читая в Документацию Kotlin по нулевой безопасности , кажется, что рекомендуемый способ развернуть дополнительные опции в Kotlin - использовать let.

Немного предыстории относительно этих четырех методов:

val string = ""
val thing: String = string.apply { length } + string.also { it.length }
val other: Int = string.run { length } + string.let { it.length }

Этот код компилируется из-за типов возврата четырех методов. Для получения дополнительной информации об этих методах, вы можете взглянуть на это сообщение в блоге

Однако, поскольку все четыре метода развернуты необязательно, некоторые разработчики в моей команде предпочли бы написать:

textView?.run {
someLongMethod(
        text = text,
        blah = blah,
        blah2 = blah2
    )
}

над

textView?.let {
someLongMethod{
        text = it.text,
        blah = it.blah,
        blah2 = it.blah2
    )
}

Вышеупомянутое предпочтение является стилистически разумным, а также непротиворечивым, поскольку в обоих случаях мы не используем тип возврата блоков let или run. Кроме того, использование run разворачивает необязательный textView, также удаляя лишнюю it. от необходимости перезаписывать в моем вызове метода.

Вся предпосылка моего аргумента основана на том факте, что приемлемо, стилистически и условно, делать вышеуказанное.

Если это так, то в данном конкретном контексте замена let на also или run на apply не приведет к изменениям ни внутри их соответствующих блоков, ни побочных эффектов.

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


Поэтому я предложил следующие два расширения (хотя второе может использовать более хорошее имя):

@UseExperimental(ExperimentalContracts::class)
inline fun <T, R> T.unwrap(block: (T) -> R) {
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    block(this)
}

@UseExperimental(ExperimentalContracts::class)
inline fun <T, R> T.unwrapInScope(block: T.() -> R) {
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    block()
}

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

textView?.unwrapInScope {
someLongMethod(
        text = text,
        blah = blah,
        blah2 = blah2
    )
}

И поскольку мы не используем тип возвращаемого значения, в этом случае предпочтительнее использовать расширение. Другое расширение unwrap будет использоваться тогда, когда let предпочтительнее, чем run.

Это, конечно, только мой аргумент. Однако мне интересно, есть ли что-то, что я пропускаю? Разве мы не должны использовать run для развёртывания опций, для начала? Или Kotlin должен был встроить различные методы, чтобы устранить это несоответствие?

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