How to cope with when exhaustive issue if i don't have nothing to return i case of else?

Viewed 205

I have a code that logically can have only 3 values. But the when assignment is on a Int variable type. What can i do, if i don't have a RecyclerView.ViewHolder object to return if non of the "cases" in the when expression won't happen (a situation that can't be). I can solve it with "ugly" solution that if 'else' so return of one of the RecyclerView.ViewHolder that i'm returning in the existing cases, but i want to know if there is more elegant way to deal with this situation. I have the following code:

 override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
    return when (viewType)
     {
         DataTypesEnum.COUNTRY_ITEM.ordinal -> CountryDataItem.onCreateViewHolder(parent)
         DataTypesEnum.LEAGUE_ITEM.ordinal -> LeagueDataItem.onCreateViewHolder(parent)
         DataTypesEnum.GAME_ITEM.ordinal ->  GameDataItem.onCreateViewHolder(parent)
     }
}

the compiler says that :

when' expression must be exhaustive, add necessary 'else' branch

2 Answers

In your case it would be okay to throw an InvalidArgumentException. It should never happen that an unknown ViewHolder type can reach this point. If that happens, something went horribly wrong and you want to get notified about that.

 override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
    return when (viewType)
     {
         DataTypesEnum.COUNTRY_ITEM.ordinal -> CountryDataItem.onCreateViewHolder(parent)
         DataTypesEnum.LEAGUE_ITEM.ordinal -> LeagueDataItem.onCreateViewHolder(parent)
         DataTypesEnum.GAME_ITEM.ordinal ->  GameDataItem.onCreateViewHolder(parent)
         else -> throw InvalidArgumentException("Invalid ViewHolder Type")
     }
}

If you want to avoid that warning you could make use of the kotlin require operator and in that way exclude values that are not allowed. But if any value that is not allowed comes into that function it would also throw an exception.

[I] want to know if there is more elegant way to deal with this situation.

If you're absolutely sure that the viewType index will be in the right range, you can get the enum from the list. Then you'll just need to switch on that enum.

override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
  return when (DataTypesEnum.values()[viewType])
  {
    DataTypesEnum.COUNTRY_ITEM -> CountryDataItem.onCreateViewHolder(parent)
    DataTypesEnum.LEAGUE_ITEM -> LeagueDataItem.onCreateViewHolder(parent)
    DataTypesEnum.GAME_ITEM ->  GameDataItem.onCreateViewHolder(parent)
  }
}

I agree with J. Hegg's response in that you should be cautious!


Update 2020-11-23 with further context:

I'm just very curious how the line (DataTypesEnum.values()[viewType]) makes all the when return statement to be exhaustive and prevent from the compiler to complain on exhaustive issue?

In your initial example you switch on an Int type.

when (viewType) {
  // ...
}

Therefore, to make the complier happy with viewTypes, you either need to cover all possible values of Int, or include an else statement in the when block.

My suggestion maps the Int type to the corresponding DataTypesEnum before switching on it. That way, the when block only needs all DataTypesEnum options accounted for to be exhaustive.

DataTypesEnum.values() returns an Array<DataTypesEnum> of all of the DataTypesEnum in order which is very similar to using the #ordinal values in your solution.

The next step is to get the value from the array, which I use the index access operator [ ].

DataTypesEnum.values()[viewType]
// is the same as
DataTypesEnum.values().get(viewType)

That should do the trick!

If you're not sure if viewType will fall into the right range, you might want to check the value so you don't hit an IndexOutOfBoundsException.

Hopefully that answers your question!

Related