Estoy trabajando con un método que devuelve diferentes tipos de objetos, he usado el retorno Cualquier tipo, pero ¿hay una mejor opción para lograr esto? Este es mi método:
override fun getNavItemById(dCSServiceContext: DCSServiceContext): Observable<Any> { return scribeProvider.getNavItemById(dCSServiceContext).map { navItem -> scribePresenter.presentNativeItem(navItem) }.toObservable() }Después estoy haciendo una conversión del objeto devuelto, usando el operador when, y estoy haciendo algo como esto:
when (item) { is NavItem -> { if (parentItem.hasChildren) { parentItem.items?.add(item) recursiveItem = item } } is Image -> { if (parentItem.hasImages) { parentItem.image = Image(item.image, item.selectedImage) recursiveItem = parentItem } } }Y mi otra duda es como puedo usar este método y extraer este tipo de objeto con otro enfoque.
¡¡¡Gracias!!!
Lo que necesita es un tipo de coproducto , como el tipo de datos Either que se encuentra en muchas bibliotecas FP populares, para Kotlin, Arrow-kt ya lo proporciona , pero también se puede hacer lo mismo usando sealed classes .
Ejemplo (clase sellada )
sealed class Result { data class A(val value: Int) : Result() data class B(val value: String) : Result() } fun intOrString(number: Int): Result = if (number%2 == 0) Result.A(number) else Result.B("~$number~") fun main(args: Array<String>) { (1..10).map(::intOrString).forEach(::println) }Producción
B(value=~1~) A(value=2) B(value=~3~) A(value=4) B(value=~5~) A(value=6) B(value=~7~) A(value=8) B(value=~9~) A(value=10)Ejemplo (cualquier tipo de datos)
fun intOrString(number: Int): Either<Int, String> = if (number%2 == 0) Left(number) else Right("~$number~") fun main(args: Array<String>) { (1..10).map(::intOrString).forEach(::println) }Producción
Right(b=~1~) Left(a=2) Right(b=~3~) Left(a=4) Right(b=~5~) Left(a=6) Right(b=~7~) Left(a=8) Right(b=~9~) Left(a=10)Kotlin presenta clases selladas, que es exactamente lo que necesita aquí. En tu ejemplo puede verse así:
sealed class NavItem { object Item : NavItem() data class Image(val image: String, val selectedImage: String) : NavItem() } ... override fun getNavItemById(dCSServiceContext: DCSServiceContext): Observable<NavItem> { ... } ... // I don't know what class declares getNavItemById function navigation.getNavItemById(serviceContext).subscribe { item -> when (item) { is Item -> { if (parentItem.hasChildren) { parentItem.items?.add(item) recursiveItem = item } } is Image -> { if (parentItem.hasImages) { parentItem.image = Image(item.image, item.selectedImage) recursiveItem = parentItem } } } } Las clases selladas pueden usarse (hasta cierto punto) como tipos de datos algebraicos de lenguajes de programación funcionales. Los uso principalmente como enumeraciones, para tipos de modelos ( ChatType , ConnectionStatus ), ver estados y resultados de solicitudes de red.
Podría pensar en hacer que su función sea genérica de esta manera:
override fun <T: Any> getNavItemById(dCSServiceContext: DCSServiceContext): Observable<T> { return scribeProvider.getNavItemById(dCSServiceContext).map { navItem -> scribePresenter.presentNativeItem(navItem) }.toObservable() } pero para su caso de uso no haría una diferencia. Usar when para distinguir entre diferentes tipos es la forma correcta de hacerlo, ya que item se emitirá de manera inteligente de manera adecuada para su uso posterior.