For a final class why does when complain?

150 Views Asked by At

So in the following code I get compilation error that "when must be exhaustive add necessary else":

class Test {

}

fun eval(e: Test): Int =
        when(e) {
            is Test -> throw IllegalArgumentException()
        }

To clarify this is only code aimed to understand Kotlin (newbie here).
So Test can not be extended by any subclass by default behaviour of Kotlin.
So what are other cases that the when expects?
Using sealed also does not work in this case

4

There are 4 best solutions below

6
On

class Test

Because when needs default value for return, if the elements (e) is not matched anything it should be return default value

class Test

fun eval(e: Test): Int =
when (e) {
    is Test -> throw IllegalArgumentException()
    else -> 7 //default value
}

other option is change return type as nullable in your function

fun eval(e: Test): Int? =
when (e) {
    is Test -> throw IllegalArgumentException()
    else -> null
        }

when

If when is used as an expression, else branch is mandatory, unless the compiler can prove that all possible cases are covered with branch conditions

.

for more details refer when-expression

if elements is single no point of use when. your function may be simplified as below

fun eval(e: Test): Int = throw IllegalArgumentException() 
0
On

So what are other cases that the when expects? - "else" because Test is not sealed class.

The error shows clearly that you need to add else condition, i tried to return 0 in else condition since the function return Integer and error gone.

class Test {

}

fun eval(e: Test): Int =
    when(e) {
        is Test -> throw IllegalArgumentException()
        else -> 0 
    }

or you need to assign to var or val and return Int value as below,

fun eval(e: Test): Int {
    val i: Int = when (e) {
        is Test -> throw IllegalArgumentException()
        else -> 0
    }
    return i
}
3
On

The problem is that when have to be exhaustive when you want to use it in return/assign statement.

For example:

// let k be of Any? type:
when (k) {
    is Number -> { println("Ok, k is a Number") }
}

This is OK. You don't expect any result. If k is a number then something will be printed. Otherwise, nothing will happen.

So, the second example:

// k is the same
boolean isNumber = when (k) {
    is Number -> true;
}

But, what if boolean is not a number? Code will not compile, because your program would be in indefinite state.

So, what you can do? Your when have to be exhaustive. So, just add else. One of the pattern is, for example, to throw IllegalStateException() if case like your.

fun eval(e: Test): Int = when(e) {
    is Test -> 1
    else -> throw IllegalArgumentException("This should never happen!")
}

Edit:

Regarding to your comment. e actually can be something different than Test. It can be... Nothing.

I am completely aware about possibilities when it can happen and correctness of usage things like this. But, example below is completely OK and compiles without any problem. Maybe with reflection it would have some useful usage.

class Test

fun eval(e: Test): Int =
    when(e) {
        is Test -> throw IllegalArgumentException()
        else -> throw IllegalArgumentException()
    }

fun getNothing(): Nothing {
    throw IllegalStateException()
}

fun main() {
    eval(getNothing())
}
0
On

Adding sealed without other changes is pointless: it makes the class abstract and impossible to subclass, so you'll never have an instance. I believe this is a compiler bug, it's very similar to https://youtrack.jetbrains.com/issue/KT-28249 though with class instead of object.

It's also a low-impact one, because if is Test is the only branch, you can just replace the entire when with the branch, and if it isn't, you can replace is Test with else.