#kotlin
#котлин
Вопрос:
У меня есть множество клиентов, у каждого клиента есть свойства id
uuid
subCustomer
и другие свойства , которые меня не интересуют. Я хотел бы выполнить одну итерацию, в которой я создал бы 3 массива , в которых содержался бы только один ids
, другой uuids
и третий subcustomers
. Я пытался добиться этого с помощью fold
такой функции:
customers.fold( mapOf( "ids" to listOflt;Stringgt;(), "uuids" to listOflt;UUIDgt;(), "subCustomers" to listOflt;Stringgt;() )) { acc, customer -gt; acc["ids"]?.plus(customer["id"]) acc["uuids"]?.plus(customer["uuid"]) acc["subCustomers"]?.plus(customer["subCustomer"]) }
С помощью этого кода я получаю ошибку в редакторе:
Type mismatch. Required: Maplt;String, Listlt;{Comparable{String amp; UUID}gt; amp; java.io.Serializable}gt;gt; Found: Listlt;Any?gt;?
Я тоже пробовал это:
customers.fold( mapOf( "ids" to listOflt;Stringgt;(), "uuids" to listOflt;UUIDgt;(), "subCustomers" to listOflt;Stringgt;() )) { acc, customer -gt; mapOf( "ids" to acc["ids"]?.plus(customer["id"]), "uuids" to acc["uuids"]?.plus(customer["uuid"]), "subCustomers" to acc["subCustomers"]?.plus(customer["subCustomer"]) ) }
Но я получаю эти ошибки:
Type mismatch. Required: Listlt;{Comparable{String amp; UUID}gt; amp; java.io.Serializable}gt; Found: Listlt;Any?gt;? Type mismatch. Required: Maplt;String, Listlt;{Comparable{String amp; UUID}gt; amp; java.io.Serializable}gt;gt; Found: Maplt;String, Listlt;Any?gt;?gt;
Комментарии:
1. Пожалуйста , не используйте карты для этих целей. Напишите классы данных.
2. Как добавить в список в классе данных?
Ответ №1:
Напишите два класса данных для ваших данных. Один для ваших клиентов и один для трех списков, которые вы хотите:
data class Customer( val id: String, val uuid: UUID, val subCustomer: String, ) data class CustomerDataLists( val ids: MutableListlt;Stringgt; = mutableListOf(), val uuids: MutableListlt;UUIDgt; = mutableListOf(), val subCustomers: MutableListlt;Stringgt; = mutableListOf(), )
Затем просто используйте простой цикл for для добавления данных в:
val dataLists = CustomerDataLists() for (customer in customers) { dataLists.ids.add(customer.id) dataLists.uuids.add(customer.uuid) dataLists.subCustomers.add(customer.subCustomer) } // now dataLists is filled with customers' data
Комментарии:
1. Я бы не советовал использовать
var
с изменяемыми списками. Я думаю, что это должно быть либоvar
сList
типом, либоval
сMutableList
типом2. @Джоффри о боже, я забыл, что оставил
var
там. Я планировал использовать неизменяемые списки, как это сделал ОП, но потом подумал, что это создаст слишком много списков.
Ответ №2:
Ответ @Sweeper хороший. Я считаю, что в любом случае стоит использовать классы данных вместо карт для такого рода вариантов использования.
Поскольку у вас на самом деле нет никаких взаимодействий между 3 списками в fold
, вы также можете создавать эти списки независимо (но здесь, конечно, 3 итерации).:
data class Customer( val id: String, val uuid: UUID, val subCustomer: String, ) data class AggregatedCustomers( val ids: Listlt;Stringgt;, val uuids: Listlt;UUIDgt;, val subCustomers: Listlt;Stringgt;, ) val customers: Listlt;Customergt; = TODO("get that list from somewhere") val aggregated = AggregatedCustomers( ids = customers.map { it.id } uuids = customers.map { it.uuid } subCustomers = customers.map { it.subCustomer } )
Ответ №3:
Этот ответ состоит из 3 частей:
- Лучший способ решить такую проблему;
- Почему исходный код не работает;
- На другие проблемы нужно обратить внимание.
1. Лучший способ решить такую проблему
Давайте предположим, что Consumer
упомянутое выглядит следующим образом:
data class Customer( val id: String, val uuid: UUID, val subCustomer: String, )
В таком случае действительно нет необходимости использовать функцию fold
. For
функции цикла или расширения forEach
просто достаточно:
val customers: Listlt;Customergt; = listOf( Customer("1", UUID.randomUUID(), "sub-1"), Customer("2", UUID.randomUUID(), "sub-2"), Customer("3", UUID.randomUUID(), "sub-3"), ) val ids = mutableListOflt;Stringgt;() // pay attention. use `mutableListOf` instead of `listOf()` val uuids = mutableListOflt;UUIDgt;() val subConsumers = mutableListOflt;Stringgt;() customers.forEach { ids = it.id uuids = it.uuid subConsumers = it.subCustomer }
2. Why the original code doesn’t work
The proposed two pieces of code are in the same pattern:
customers.fold( mapOf( "ids" to listOflt;Stringgt;(), "uuids" to listOflt;UUIDgt;(), "subCustomers" to listOflt;Stringgt;() ) ) { acc, customer -gt; // ... do something with acc and customer }
Сначала мы должны прояснить, что последнее утверждение в fold
области является выражением, которое необходимо накопить. Это как acc_n lt;combinegt; customer -gt; acc_(n 1)
для каждого клиента в customers
каждый момент времени , где lt;combinegt;
мы пишем нашу логику. Таким образом, первый предложенный фрагмент кода не работает, потому что вы можете не знать, что что-то должно быть возвращено во время написания:
customers.fold(...){ acc, customer -gt; acc["ids"]?.plus(customer.id) acc["uuids"]?.plus(customer.uuid) acc["subCustomers"]?.plus(customer.subCustomer) }
Фактически, последнее утверждение
acc["subCustomers"]?.plus(...)
является выражением с типомListlt;Anygt;?
, котлин рассматривает его как ваше «acc_(n 1)», но вы предлагаетеmapOf("ids" to ...)
какacc_0
, у которого есть типMaplt;String, ...gt;
: не тот же тип,Listlt;Anygt;?
что и . И вот почему вы получили первую ошибку:Type mismatch. Required: Maplt;String, Listlt;{Comparable{String amp; UUID}gt; amp; java.io.Serializable}gt;gt; Found: Listlt;{Comparable{String amp; UUID}gt; amp; java.io.Serializable}gt;?
Мы поговорим об универсальных типах позже.
Давайте перейдем ко второму фрагменту кода. В качестве последнего выражения в области действия предлагается fold
карта , которая также является картой:
customers.fold(...) { acc, customer -gt; mapOf( "ids" to acc["ids"]?.plus(customer.id), "uuids" to acc["uuids"]?.plus(customer.uuid), "subCustomers" to acc["subCustomers"]?.plus(customer.subCustomer) ) }
Самый простой способ устранить ошибку-использовать !!
выражение (не рекомендуется!):
customers.fold(...) { acc, customer -gt; mapOf( "ids" to acc["ids"]?.plus(customer.id)!!, "uuids" to acc["uuids"]?.plus(customer.uuid)!!, "subCustomers" to acc["subCustomers"]?.plus(customer.subCustomer)!! ) }
Причина в том, что kotlin не может утверждать, что acc[«идентификаторы»] не является нулевым, поэтому вы используете ?.
для вызова метода, безопасного для нуля. Однако такой вызов делает возвращаемый тип обнуляемым:
val cus: Customer? = Customer("1", UUID.randomUUID(), "sub-1") // cus has type Customer? : nullable val id1: String = cus?.id // [compile error] Type mismatch. [Required: String] [Found: String?] val id2: String? = cus?.id // OK val id3: String = cus?.id!! // If `cus?.id` is null, throw NPE.
Вы объявили acc_0
(в скобках после fold
) в типе Maplt;String, Listlt;Tgt;gt;
неявно (мы поговорим о T позже). Просто знайте, что T не является типом, допускающим обнуление), но карта с типом Maplt;String, Listlt;Tgt;?gt;
была найдена как acc_(n 1)
. Несоответствие типов и ошибка были показаны:
Type mismatch. Required: Listlt;{Comparable{String amp; UUID}gt; amp; java.io.Serializable}gt; Found: Listlt;{Comparable{String amp; UUID}gt; amp; java.io.Serializable}gt;?
3. Другая проблема, на которую нужно обратить внимание
Важная проблема заключается в следующем: что это за тип acc_0
?
// acc_0: mapOf( "ids" to listOflt;Stringgt;(), "uuids" to listOflt;UUIDgt;(), "subCustomers" to listOflt;Stringgt;() )
Конечно, тип каждого выражения слева to
-это строка, и Listlt;Tgt;
это тип каждого выражения справа от нее. так и должно быть Maplt;String, Listlt;Tgt;gt;
. О чем T
? Котлин пытается найти ближайшего предка String и UUID и найти их обоих, реализующих Comparablelt;?gt;
и Serializable
, так что это то, что вы видите в ошибке. Это тип Т:
Required: Listlt;{Comparable{String amp; UUID}gt; amp; java.io.Serializable}gt;
This may lead to some unwanted experience:
val map = mapOf( "listA" to mutableListOf("233"), "listB" to mutableListOf(UUID.randomUUID()) ) val listA = map["A"]!! // MutableListlt;out {Comparable{String amp; UUID}gt; amp; java.io.Serializable}!gt; // generic type "collapse" into `Nothing` for no type can implement both Comparablelt;Stringgt; and Comparablelt;UUIDgt; listA.add(Any()) // Type mismatch. [Required: Nothing] [Found: Any]
So try not to put lists with different generic type into one map.
Another problem is, when you try to invoke acc["ids"]?.plus(customer.id)
, you are actually invoking such method (from kotlin _Collections.kt
)
public operator fun lt;Tgt; Collectionlt;Tgt;.plus(element: T): Listlt;Tgt; { val result = ArrayListlt;Tgt;(size 1) result.addAll(this) result.add(element) return result }
Новый список создается каждый раз, когда вы вызываете метод! Попробуйте использовать mutableListOf()
в замене listOf()
для коллекций, в которые вы хотите внести изменения, и вместо этого используйте оператор » =» (или ?.plusAsign()
в качестве нулевой безопасной версии). Это может привести к какой-то другой проблеме с исходным кодом (которая слишком сложна, чтобы объяснить, почему), но для кода в части 1: Лучший способ решить такую проблему— =
фактически вызвать:
public inline operator fun lt;Tgt; MutableCollectionlt;in Tgt;.plusAssign(element: T) { this.add(element) }
которые просто добавляют ценность в список, не создавая новых.