How to assign a zero to genric Number in Kotlin?

139 Views Asked by At

I'm new to Kotlin, and I'm trying to write a simple generic method but it doesn't compile

inline fun <T:Number> List<T>.firstOrZero() : T {
    if (isEmpty())
        return 0   // this line doesn't compile
    else
        return this[0]
}

Is there a way to do it?

4

There are 4 best solutions below

5
broot On BEST ANSWER

We can't really handle this case in a fully generic way in Kotlin. Whatever solution we choose, we have to handle each type of Number separately.

Therefore, I suggest to not go with a generic function, but with multiple overloads:

fun List<Int>.firstOrZero() = if (isEmpty()) 0 else this[0]
fun List<Long>.firstOrZero() = if (isEmpty()) 0 else this[0]

This should be potentially faster than generic solutions using reflection, because the type is resolved at the compile time instead of runtime.

If you have more cases like this, your cases are more complicated, etc. and you are concerned about the code duplication, you can inline the common code:

fun List<Int>.firstOrZero() = firstOrDefault { 0 }
fun List<Long>.firstOrZero() = firstOrDefault { 0 }

private inline fun <T:Number> List<T>.firstOrDefault(default: () -> T) : T {
    if (isEmpty())
        return default()
    else
        return this[0]
}

But for the specific case of firstOrZero() I think it is an overkill.

7
Saxon On

You need to ensure that the return type matches the expected type.

Because you defined the generic type 'T', you need to return 'T', which extends 'Number'.

inline fun <T : Number> List<T>.firstOrZero(): T {
    if (isEmpty())
        return 0 as T
    else
        return this[0]
}
2
Ivo On

You will need to handle it for every single Number subclass you want to support. For example like this

inline fun <reified T : Number> List<T>.firstOrZero(): T {
    return (if (isEmpty()) when (T::class) {
        Int::class -> 0
        Double::class -> 0.0
        Float::class -> 0f
        BigInteger::class -> BigInteger.ZERO
        else -> throw RuntimeException()
    }
    else
        this[0]) as T
}
3
Adnan Akib On

There is a way to do it. You can use the default keyword to provide a default value for the T type parameter. In this case, the default value will be 0. The following code will compile and run:

inline fun <T:Number> List<T>.firstOrZero() : T {
    if (isEmpty())
        return 0.default<T>()   // this line compiles
    else
        return this[0]
}

The default keyword can be used to provide a default value for any type parameter. This can be useful when you want to make sure that a method or property always has a value, even if the type parameter is not specified.

In this case, the default keyword is used to provide a default value of 0 for the T type parameter. This means that if the T type parameter is not specified, the method will return 0.

Here is an example of how to use the firstOrZero() method:

val list = listOf(1, 2, 3)
val firstNumber = list.firstOrZero()
println(firstNumber) // prints 1

val emptyList = emptyList<Int>()
val firstNumberInEmptyList = emptyList.firstOrZero()
println(firstNumberInEmptyList) // prints 0