Предположим, у меня есть класс с двумя экземплярами:
class Car(val id: Int, val color: Color, val pistons: Int, val spoiler: Boolean)
val audi = Car(1234, Color.BLUE, 8, false)
val bmw = Car(4321, Color.WHITE, 6, false)
Теперь я хотел бы проверить равенство для некоторых свойств (не для всех -> в этом случае я бы использовал класс данных)
fun looksSimilar(a: Car, b: Car) = a.color == b.color && a.spoiler == b.spoiler
Сейчас я ищу метод, который делает сравнение:
T
и их свойствУ меня появилось следующее предложение:
fun <T> Pair<T, T>.equalIn(vararg arguments: (T) -> Any?) =
arguments.toList().all { it(first) == it(second) }
что позволяет мне написать вышеуказанную проверку как
val looksSimilar = (audi to bmw).equalIn({it.color}, {it.spoiler})
Кто-нибудь знает лучшее (например, более чистое/быстрое) решение?
Мой вариант использования следующий:
Я пишу приложение для Android с несколькими RecyclerView's
(= причудливый вид для отображения списков)
У каждого RecyclerView
есть ListAdapter
(отвечает за базовый список)
Для каждого ListAdapter
требуется DiffUtil.ItemCallback
(для сравнения старых и новых элементов и внесения соответствующих изменений в представление)
val callback = object : DiffUtil.ItemCallback<Car>() {
override fun areItemsTheSame(oldItem: Car, newItem: Car): Boolean
// usually checks for id, e.g. oldItem.id == newItem.id
override fun areContentsTheSame(oldItem: Car, newItem: Car): Boolean
// checks if two items look the same.
// Used for fancy enter/exit animations afaik.
// e.g. (oldItem to newItem).equalIn({it.color}, {it.spoiler})
}
На сайте использования было бы чище написать .equalIn(Car::color, Car::spoiler)
. В остальном ваш способ кажется мне приемлемым. Может быть, немного неудобно требовать промежуточную пару. Помимо этого, это создает функциональные объекты и массивы для проверки, так что есть немного оттока GC, которого у вас не было бы при непосредственном использовании ==
сравнений.
Вам не нужно ничего из этого, если вместо использования только class
вы используете data class
, именно поэтому data class
существует, это позволяет структурное сравнение, просто говоря a == b
, ваш Pistons
также должен быть классом данных. И обычно в обратном вызове элемента сравнения areItemsTheSame
это сравнение идентификаторов, а затем используемого вами контента oldItem == newItem
@vatbub я согласен, что на данный момент это не идеально для чтения; но это часть моего вопроса
@ Tenfour04 Я не знал, что смогу это сделать, спасибо +1
@cutiko, если честно, я не слишком знаком с ItemCallback. В документации areContentsTheSame
говорится: «Например, если вы используете DiffUtil с RecyclerView.Adapter, вы должны вернуть, совпадают ли визуальные представления элементов». Для меня это не обязательно включает количество поршней в моем примере
Лично вместо функции расширения я бы, вероятно, сделал какой-то класс сравнения (например, Comparator
, который обрабатывает только equals
), чтобы вы могли создать его экземпляр, настроить, как вам нравится, и просто использовать его для всех ваших проверок. Вы можете передать свой список свойств во время строительства, чтобы определить все проверки, которые он должен выполнять, а затем вы создаете его один раз - и у вас могут быть разные варианты, если они вам нужны.
Просматривая историю StackOverflow, я снова нашел этот вопрос, и он щекотал мне мозг, поэтому я немного подумал об этом. Я по-прежнему придерживаюсь своего мнения, что я предпочитаю явные проверки if (проще понять IMO, самая быстрая производительность), но если бы мне пришлось делать это с помощью функции расширения, я бы предпочел использовать список вместо пары (допуская более двух inputs), и я бы использовал справочный синтаксис для лямбда-выражений на сайте вызова, сделав его немного более кратким:
fun <T> List<T>.equalIn(vararg arguments: (T) -> Any?): Boolean {
if (isEmpty()) return false
val argumentsList = arguments.toList()
return all { item -> argumentsList.all { it(item) == it(first()) } }
}
fun main() {
val audi = Car(1234, Color.BLUE, 8, false)
val bmw = Car(4321, Color.BLUE, 6, false)
println(listOf(audi, bmw).equalIn(Car::color, Car::spoiler))
}
Приятно видеть, что вопрос годовой давности все еще щекочет ваш мозг :D
Я бы использовал if (isEmpty()) return true
, хотя.
Tbh, я бы предпочел простые глупые проверки на равенство чему-то более причудливому, потому что все знают, как они выглядят и работают. Мне потребовалось почти 2 минуты, чтобы полностью понять, что делает ваша функция и как она работает. Если бы вместо этого были только простые проверки на равенство, я бы понял их за одну секунду при чтении кода.