I have a Kotlin code snippet used for Jetpack Comp...
# codereview
k
I have a Kotlin code snippet used for Jetpack Compose navigation. It defines a sealed class hierarchy representing different screens with or without parameters, along with navigation functionalities. I want to refactor this code to adhere to better Kotlin coding practices and naming conventions suitable for Jetpack Compose navigation. How can I improve this code to make it more idiomatic and maintainable?
ScreenWithParams
Copy code
interface ScreenWithParams {
    val params: List<String>
}
NavigationDestination
Copy code
sealed class NavigationDestination(private val route: String) {

    private val paramNames: List<String>
        get() = when (this) {
            is ScreenWithParams -> params
            else -> emptyList()
        }

    val fullRoute: String
        get() = buildRoute(true)

    private val navigateRoute: String
        get() = buildRoute(false)

    private fun buildRoute(includeParams: Boolean): String {
        val params = if (includeParams && paramNames.isNotEmpty()) {
            paramNames.joinToString("/") { "{$it}" }
        } else {
            paramNames.joinToString("/")
        }
        return if (params.isNotEmpty()) "$route/$params" else route
    }

    fun navigateWithValues(vararg values: String): String {
        require(values.size == paramNames.size) {
            "Number of values must match number of parameters for $route"
        }
        return buildRoute(true).let { routeWithValues ->
            paramNames.foldIndexed(routeWithValues) { index, acc, paramName ->
                acc.replace("{$paramName}", values[index])
            }
        }
    }
}
DifferentScreens
Copy code
sealed class DifferentScreens(route: String) : NavigationDestination(route) {
    object ScreenA : DifferentScreens("ScreenA"), ScreenWithParams {
        override val params = listOf("firstName")
    }

    object ScreenB : DifferentScreens("ScreenB")
    object ScreenC : DifferentScreens("ScreenC")

    object ScreenD : DifferentScreens("ScreenD"), ScreenWithParams {
        override val params = listOf("firstName", "lastName")
    }
}
main
Copy code
fun main() {
    val screenA = DifferentScreens.ScreenA
    println("screenA startDestination with param => ${screenA.fullRoute}")
    println("screenA navigate route with param => ${screenA.navigateWithValues("Devil")}")
    println("----------------------------------------")
    val screenB = DifferentScreens.ScreenB
    println("screenB startDestination without param => ${screenB.fullRoute}")
    println("----------------------------------------")
    val screenC = DifferentScreens.ScreenC
    println("screenC startDestination without param => ${screenC.fullRoute}")
    println("----------------------------------------")
    val screenD = DifferentScreens.ScreenD
    println("screenD startDestination with param => ${screenD.fullRoute}")
    println("screenD navigate route => ${screenD.navigateWithValues("1234", "May")}")
}
Output is
Copy code
screenA startDestination with param => ScreenA/{firstName}
screenA navigate route with param => ScreenA/Devil
----------------------------------------
screenB startDestination without param => ScreenB
----------------------------------------
screenC startDestination without param => ScreenC
----------------------------------------
screenD startDestination with param => ScreenD/{firstName}/{lastName}
screenD navigate route => ScreenD/1234/May