Estoy tratando de leer una lista de objetos de la base de datos y asignarla a otro tipo de lista.
// Returns either a Failure or the expected result suspend fun getCountries(): Either<Failure, List<CountryItem>> { // Get the result from the database val result = countryLocalDataSource.getCountries() // Left means Failure if (result.isLeft) { // Retrieve the error from the database lateinit var error: Failure result.either({ error = it }, {}) // Return the result return Either.Left(error) } // The database returns a List of Country objects, we need to map it to another object (CountryItem) val countryItems: MutableList<CountryItem> = mutableListOf() // Iterate the Country List and construct a new List of CountryItems result.map { countries -> { countries.forEach { // Assign some values from an Enum (localized string resources) val countryEnumValue = Countries.fromId(it.id) countryEnumValue?.let { countryIt -> val countryStringNameRes = countryIt.nameStringRes; // Create the new CountryItem object (@StringRes value: Int, isSelected: Bool) countryItems.add(CountryItem(countryStringNameRes, false)) } } } } // Because this is a success, return as Right with the newly created List of CountryItems return Either.Right(countryItems) } En aras de la legibilidad, no incluí todo el Repository o las clases DAO y dejé comentarios en el fragmento de código anterior.
En pocas palabras : estoy usando Coroutines de Kotlin para acceder a la base de datos en un hilo separado y estoy manejando la respuesta en el UI Thread de usuario. Usar la clase Cualquiera para devolver dos resultados diferentes (fracaso o éxito).
El código anterior funciona, sin embargo, es demasiado feo. ¿Es este el enfoque correcto para entregar el resultado?
Lo que estoy tratando de hacer es refactorizar el código anterior.
Todo el problema es causado por los dos tipos de objetos diferentes. La Database Data Source API la base de datos devuelve un Either<Failure, List<Country>> , mientras que se espera que la función devuelva un Either<Failure, List<CountryItem>> .
No puedo entregar un List<CountryItem> directamente desde la Database Data Source API de la base de datos, porque Android Studio no me permite compilar el proyecto (entidades que implementan interfaces, error de compilación, etc.). Lo que estoy tratando de lograr es mapear el resultado de Either de una manera más agradable.
Intenta usar el resultado de Kotlin
Así que en tu caso puedes escribir algo como:
return result.mapCatching { list: List<Country> -> /*return here List<CountryItem>>*/ }Y para comprobar el resultado de la llamada:
result.fold( onSuccess = {...}, onFailure = {...} ) Para invocar un constructor debe llamar a Result.success(T) o Result.failure(Throwable)
Desafortunadamente, también deberá suprimir las advertencias de uso como tipo de devolución Cómo
Puede simplificar comprobando el tipo de Either y accediendo directamente al valor. En tu caso:
acceda a Left a través result.a -> Failure
acceda a Right a través result.b -> List<Country>
ex:
when (result) { is Either.Left -> { val failure: Failure = result.b ... } is Either.Right -> { val countries: List<Country> = result.b ... } } Una alternativa es usar la función either() (normalmente esto se llama fold() ):
result.either( { /** called when Left; it == Failure */ }, { /** called when Right; it == List<Country> */ } )Suponga que su clase de Country se define de la siguiente manera:
data class Country(val name: String) {} y su clase CountryItem se define de la siguiente manera:
data class CountryItem(private val name: String, private val population: Int) {} y su clase CountryLocalDataSource con un método getCountries() como este:
class DataSource { suspend fun getCountries(): Either<Exception, List<Country>> { return Either.Right(listOf(Country("USA"), Country("France"))) //return Either.Left(Exception("Error!!!")) } }entonces la respuesta a tu pregunta seria:
suspend fun getCountryItems(): Either<Exception, List<CountryItem>> { when (val countriesOrFail = DataSource().getCountries()) { is Either.Left -> { return Either.Left(countriesOrFail.a) } is Either.Right -> { val countryItems = countriesOrFail.b.map { CountryItem(it.name, 1000) } return Either.Right(countryItems) } } } Para llamar a su getCountryItems() , aquí hay un ejemplo:
suspend fun main() { when (val countriesOrFail = getCountryItems()) { is Either.Left -> { println(countriesOrFail.a.message) } is Either.Right -> { println(countriesOrFail.b) } } }Aquí está el código de muestra en el patio de recreo: https://pl.kotl.in/iiSrkv3QJ
Una nota sobre su función de mapa:
Supongo que en realidad no necesita que el resultado sea MutableList<CountryItem> pero tuvo que definirlo porque desea agregar un elemento a medida que itera a través de la lista de entrada List<Country> .
Quizás el siguiente sea el caso: si tiene una List<Country> con 2 elementos como en el ejemplo, y desea mapear para que el resultado se convierta en una List<CountryItem> con también 2 elementos correspondientes, entonces no necesita para llamar a forEach dentro de un fun que se pasa al map de funciones de orden superior. Pero esta puede ser una pregunta completamente nueva.