Using an enum with the when statement to avoid exhustive error


#1

I have the following snippet that is used for the FragmentStatePageAdapter. I have 2 conditions in the when statement. However, I am trying to avoid the use of the else branch. As I don’t think I want to be return the LoginFragment(). However, if I don’t add the else branch I get the exhaustive error. Is there anything I can do to avoid the use of the else branch?

class LoginScreenPagerAdapter(fragmentManager: FragmentManager)
    : FragmentStatePagerAdapter(fragmentManager) {

    private val MAX_NUMBER_OF_SCREENS = 2

    override fun getItem(position: Int): Fragment {
        return when (position) {
            LOGIN_FRAGMENT.ordinal -> LoginFragment()
            SIGNUP_FRAGMENT.ordinal -> SignupFragment()
            else -> {
                LoginFragment()
            }
        }
    }

    override fun getCount() = MAX_NUMBER_OF_SCREENS

    enum class FragmentPosition(position: Int) {
        LOGIN_FRAGMENT(0),
        SIGNUP_FRAGMENT(1)
    }
}

I have also tried using the following with sealed classes and I am getting the same issue. Anything with the way I have constructed the when statement with sealed classes?

class LoginScreenPagerAdapter(fragmentManager: FragmentManager)
    : FragmentStatePagerAdapter(fragmentManager) {

    private val MAX_NUMBER_OF_SCREENS = 2

    override fun getItem(position: Int): Fragment {
        return when (position) {
            FragmentPositionSealed.Login().position -> LoginFragment()
            FragmentPositionSealed.SignUp().position -> SignupFragment()
            else -> {
                return LoginFragment()
            }
        }
    }

    override fun getCount() = MAX_NUMBER_OF_SCREENS

    sealed class FragmentPositionSealed {
        class Login(val position: Int = 0) : FragmentPositionSealed()
        class SignUp(val position: Int = 1) : FragmentPositionSealed()
    }
}

#2

Convert your position Int to a FragmentPosition before switching on it.

For example:

when (position.toFragmentPosition()) {
    LOGIN_FRAGMENT -> LoginFragment()
    SIGNUP_FRAGMENT -> SignupFragment()
}

...

private fun Int.toFragmentPosition() = when (this) {
    LOGIN_FRAGMENT.ordinal -> LOGIN_FRAGMENT
    SIGNUP_FRAGMENT.ordinal -> SIGNUP_FRAGMENT
    else -> {
                // a sensible default or throw an exception
    }
}

#3

I personally prefer to throw NotImplementedError(), with a message indicating details necessary for quick identification.

I would rather that the app crashes on an unexpected input (to be caught in testing) than have it blithely do the wrong thing,

Otherwise you could have just done the following:

        return when (position) {
            FragmentPositionSealed.SignUp().position -> SignupFragment()
            else -> LoginFragment()            
        }

Which effectively assumes that LoginFragment is a sensible default.


#4

But then you have to repeat this knowledge about the default wherever you have an position as Int. In my solution, this knowledge about the default/exception is encapsulated within 1 function.