Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there a way in Kotlin to combine an inline function and a when expression with an extension function?

I would like to reduce the size of an if-elseif-else ladder but can't use just when because the smartcast doesn't catch the cases I'm dealing with. I'm wondering if I can combine something like a let or an also and a when into a single inline function to solve this.

I've tried using smart casting with is but it's problematic because I'd have to do a when within the outer when to actually get the result I wanted. I ended up doing something similar to one of the responses from this post: Kotlin and idiomatic way to write, 'if not null, else...' based around mutable value by just having a let block acting on the non-null variable then executing the when block inside that let.

Case I'm currently going with:

variable?.let { safeVariable ->
    when {
        case1 -> doSomething(safeVariable)
        case2 -> doSomethingElse(safeVariable)
         ...
        else -> catchAll(safeVariable)
    }
    return@let
}
Log.e(TAG, "variable was null")

Cases I've considered:

when(variable) {
    is Type -> when {
                   case1 -> doSomething(variable)
                   case2 -> doSomethingElse(variable)
                   ...
                   else -> catchAll(variable)
               }
   else -> Log.e(TAG, "variable was null")
}
if (variable is Type) {
    when {
        case1 -> doSomething(variable)
        case2 -> doSomethingElse(variable)
         ...
        else -> catchAll(variable)
    }
} else {
    Log.e(TAG, "variable was null")
}

What I'd like to be able to write would look like this:

variable?.alsoWhen { safeVariable ->
    case1 -> doSomething(safeVariable)
    case2 -> doSomethingElse(safeVariable)
     ...
    else -> catchAll(safeVariable)
} ?: Log.e(TAG, "variable was null")

Is this possible to write in Kotlin with an extension function and if not is there at least an alternative to what I wrote above to make it more compact and readable?

EDIT:

Based on Damon's comment below I did think of a slightly cleaner way to approach this with:

when(true) { 
    variable?.case1 -> doSomething(variable) 
    variable?.case2 -> doSomethingElse(variable) 
     ... 
    variable is String -> catchAll(variable)
    else -> Log.e(TAG, "variable was null") 
} 

It would be nice to get rid of the (true) next to the when if possible but this is clean enough that I'm pretty happy with the solution.

like image 722
Ryan Shea Avatar asked Nov 06 '22 18:11

Ryan Shea


1 Answers

You can perform the cast inside the argument of the when statement.

E.g.

when (variable as? Type) {
    case1 -> ...
    case2 -> ...
    null -> ...
    else -> ...
}

Another example based on comment:

enum class UrlType { TYPE_1, TYPE_2, TYPE_3, NULL }

fun String?.urlType(): UrlType {
    return when {
        this?.contains("...") == true -> UrlType.TYPE_1
        this?.startsWith("...") == true -> UrlType.TYPE_2
        this != null -> UrlType.TYPE_3
        else -> UrlType.NULL
    }
}

when (variable.urlType()) {
    UrlType.TYPE_1 -> doSomething()
    UrlType.TYPE_2 -> doSomethingElse()
    UrlType.TYPE_3 -> ...
    UrlType.NULL -> ...
}
like image 85
Damon Baker Avatar answered Dec 04 '22 15:12

Damon Baker