9

As per documentation of When in Kotlin, else is not mandatory if the compiler knows all the values are covered. This is very in case of emums or sealed class but how to do it in case of arrays for numbers 1 to 5 (startRating).

private fun starMapping(startRating: Int): String {

    return when (startRating) {
        1 -> "Perfect"
        2 -> "Great"
        3-> "Okay"
        4-> "Bad"
        5-> "Terrible"
        // don't want to add else as I believe it is prone to errors.
    }
}

Something similar to this

return when (AutoCompleteRowType.values()[viewType]) {
        AutoCompleteRowType.ITEM -> ItemView(
                LayoutInflater.from(parent.context).inflate(R.layout.item_venue_autocomplete_item_info, parent, false))

        AutoCompleteRowType.SECTION -> SectionView(
                LayoutInflater.from(parent.context).inflate(R.layout.item_venue_autocomplete_section, parent, false)
        )
    }
Willi Mentzel
  • 27,862
  • 20
  • 113
  • 121
Code_Life
  • 5,742
  • 4
  • 29
  • 49
  • 7
    _"I believe it is prone to errors"_. So use `else -> throw IllegalArgumentException("Illegal startRating")` (this is how it's done on the [official kotlin idioms page](https://kotlinlang.org/docs/reference/idioms.html)). – Michael Aug 04 '20 at 08:32
  • @Michael: I don't want to write the else statement altogether, similar to enums check the edit. – Code_Life Aug 04 '20 at 08:36
  • 1
    Why don't you want to write the `else` clause? What errors are you expecting? You would have to write an `enum` if you want it to be obsolete... An `enum` seems more suitable for star ratings than `Int`s anyway. – deHaar Aug 04 '20 at 08:46
  • There is no way to guarantee that an invalid rating will never be passed to this method. You could make a typo, you could receive invalid data from the user, some external code could invoke this method reflectively, etc. The safest option is to simply throw an error, but the easiest would probably be to consider using enums. – Salem Aug 04 '20 at 08:52
  • 1
    You'd need type of `startRating` to be "`Int` from 1 to 5", and there's no such type in Kotlin. – Alexey Romanov Aug 04 '20 at 09:26

2 Answers2

13

Using when statement it is impossible to exclude else clause in case of using ints, because compiler doesn't know what to return if startRating is not in 1..5 range. You can, for example, throw an IllegalStateException if the value is not in the required range:

private fun starMapping(startRating: Int): String {
    return when (startRating) {
        1 -> "Perfect"
        2 -> "Great"
        3-> "Okay"
        4-> "Bad"
        5 -> "Terrible"
        else -> throw IllegalStateException("Invalid rating param value")
    }
}

Or you can do something like this:

return when {
    startRating <= 1 -> "Perfect"
    startRating == 2 -> "Great"
    startRating == 3 -> "Okay"
    startRating == 4 -> "Bad"
    else -> "Terrible"
}

But else clause is required.

Sergio
  • 27,326
  • 8
  • 128
  • 149
  • If you don't want to throw an exception, the next-best option is probably to return `null`; that way, the caller can use e.g. the elvis or safe-call operators to invalidate whatever it's being used in. – gidds Aug 04 '20 at 09:53
  • Since the function is just implementing a mapping, I'd also look at storing an actual map or array of strings; the function then collapses to a simple lookup, and you can use `.getOrNull()` to do that safely. – gidds Aug 04 '20 at 09:56
4

You may not want to use when for this at all. Here is what I would suggest:

You could create an enum class like so:

enum class Rating(val score: Int) {
  Perfect(1),
  Great(2),
  Okay(3),
  Bad(4),
  Terrible(5)
}

And utilise it like that:

fun ratingForScore(score: Int) = Rating.values().firstOrNull {
    it.score == score
}?.toString()

ratingForScore(1) // "Perfect"
Willi Mentzel
  • 27,862
  • 20
  • 113
  • 121