Поражение в programming in small
Задача — сделать композитный ключ для записи кэша, состоящий из некоторых идентификаторов переданных сущностей.
В чем разница между этим:
val cacheKey = ids.joinToString { "," }
и этим?
val cacheKey = ids.joinToString(",")
Очевидно! Во втором случае нет бага на проде 😺
Чуть подробнее для тех, кто не знаком с синтаксисом Kotlin. Разница в том, какому аргументу передаётся
злосчастная ",". Посмотрим на сигнатуру метода:
public fun <T> Iterable<T>.joinToString(
separator: CharSequence = ", ", // еще один сахарок -- именованные аргументы
prefix: CharSequence = "",
postfix: CharSequence = "",
limit: Int = -1,
truncated: CharSequence = "...",
transform: ((T) -> CharSequence)? = null
): String
Kotlin любезно даёт нам синтаксический сахар в виде trailing lambda: когда лямбда передаётся
последним и единственным аргументом функции, то не требуется избыточных круглых скобок. В нашем случае это
возможно, так как все аргументы до лямбды transform имеют дефолтные значения:
val cacheKey = ids.joinToString(
transform = { element -> "," } // переопределили дефолтный null
)
listOf(10, 20, 30).joinToString { "," } // ",, ,, ,"
Таким образом каждый элемент ids заменяется строкой ",", а между элементами
остаётся стандартный разделитель separator: CharSequence = ", ".
Во втором примере мы видим уже классическую передачу первого аргумента separator:
val cacheKey = ids.joinToString(
separator = "," // переопределили дефолтный ", "
)
listOf(10, 20, 30).joinToString(",") // "10,20,30"
Вот такая обидная ошибка из-за невнимательности. "Но тесты должны были это отловить!" — возразите вы мне. Здесь уже не обойтись обычным тестом "положить значение в кэш по динамически сгенерированному ключу, а затем по нему же достать положенное ранее значение". Нужно при доставании явно прописать статичную ожидаемую ключ-строку ручками.
Несложно, но нужно не забыть написать ещё и такой тест. В связи с этим в планах формализовать работу с кэшем (и не только), чтобы у разработчиков не было возможности ошибиться. Как минимум при сборке проекта требовать тесты на каждую новую мапу в кэше.
Ну и как вывод можно сказать, что человек в отличие от ИИшки в таких мелочах будет всегда ошибаться. Сложно держать в голове в актуальном состоянии сигнатуры и поведение всех используемых методов + фокусироваться на их локальном применении. Особенно с учётом того, что номера версий библиотек и фреймворков неумолимо растут. Сдаём позиции в programming in small, получается!
← Назад к списку статей