Why do generic functions can't have default arguments?


I wrote a helper function to work with JUnit4:

fun <T : Exception> expectException(kClass: KClass<T>, assert: () -> Unit) {
    val exceptionName = kClass.simpleName
    try {
        Assert.fail("Expect to throw [$exceptionName] exception.")
    } catch (e: Exception) {
        if (!kClass.java.isInstance(e)) {
            if (e is AssertionError) {
                throw e
            } else {
                Assert.fail("Expect to throw [$exceptionName] exception but was [${e.javaClass.simpleName}].")

It works, but I want to make it better which can be called like this:


So I tried

fun <T : Exception> expectException(kClass: KClass<T> =Exception::class, assert: () -> Unit){

But got a Kotlin: Type mismatch: inferred type is KClass<Exception> but KClass<T> was expected

I must do this like I used to do in JAVA:
fun expectThrows(assert: () -> Unit) = expectThrows(Exception::class, assert)

So why do generic functions can’t have default arguments?


Generic functions can definitely have default arguments, but not in the way you’re using them. You’re saying "I want to have a function that can have any exception type as a parameter, and if I don’t specify the parameter value, it will be Exception::class". Now suppose that you’re calling the function in this way:

expectThrows<IllegalArgumentException> { ... }

In this call, the type of the kClass parameter will be KClass<IllegalArgumentException>, and the value will be Exception::class. Exception is not a subclass of IllegalArgumentException, so the code will not be valid, and the compiler rejects it.

To fix the problem, note that you don’t actually need the function to be generic; you’re not using T in the function body at all. Therefore, you can change the function to:

fun expectException(kClass: KClass<out Exception> = Exception::class, assert: () -> Unit) { ... }


@zhangdatou You might be interested in how we’ve implemented the similar function in our kotlin-test library: https://github.com/JetBrains/kotlin/blob/master/libraries/kotlin.test/shared/src/main/kotlin.jvm/kotlin/test/TestAssertionsJVM.kt#L8


Thanks, I fixed my code, then added a “catch” for fun.

Now I can write tests like this:

expectThrows(ArrayIndexOutOfBoundsException::class) {
} catch { e ->
    Assert.assertEquals("1", e.message)


Here’s my code:

fun <T : Throwable> expectThrows(kClass: KClass<T>, block: () -> Unit): ExpectThrows<T> {
        val exceptionName = kClass.simpleName
        try {
        } catch(e: Throwable) {
            if (!kClass.java.isInstance(e)) {
                Assert.fail("Expected an exception of type $exceptionName to be thrown, but was ${e.javaClass.simpleName}")
            } else {
                return ExpectThrows(e as T)
        Assert.fail("Expected an exception of type $exceptionName to be thrown, but was completed successfully.")

    class ExpectThrows<out T : Throwable> internal constructor(val exception: T) {
        infix fun catch(assertion: (e: T) -> Unit) {

Many thanks.


It can be written as

inline fun <reified T : Exception> expectException(kClass: KClass<T> = T::class, assert: () -> Unit)

But inline function is not my expectation in some cases. Can we support “reified” feature just for the default arguments? I mean that I don’t need to declare “inline” and “reified” in this case. Because I think when compiling source codes, compiler understands what “T” is .