Daniel Wolf
Daniel Wolf

Reputation: 13673

Generic function for all number-like values?

I'd like to write a generic Kotlin function that works with arbitrary types, as long as they support some basic numeric operations, such as comparison and addition. Something like this (note: this code doesn't compile):

fun <T : ???> twiceTheLarger(a: T, b: T) = if (a > b) a + a else b + b;

In C++, this kind of code works. The following C++ twiceTheLarger function takes anything that supports the + and > operators, be it a primitive numeric type or a custom class:

#include <iostream>

template <typename T> T twiceTheLarger(T a, T b) {
  return a > b ? a + a : b + b;
}

int main() {
  std::cout << twiceTheLarger(1, 2) << std::endl; // int values
  std::cout << twiceTheLarger(42.0, 1.0) << std::endl; // double values
}

How can I get a similar result in Kotlin? The best way I could come up so far is to explicitly pass an object with the required functions, like this:

interface NumericOperators<T> {
    fun plus(a: T, b: T): T
    fun greaterThan(a: T, b: T): Boolean
}

object IntNumericOperators : NumericOperators<Int> {
    override fun plus(a: Int, b: Int) = a + b
    override fun greaterThan(a: Int, b: Int) = a > b
}

object DoubleNumericOperators : NumericOperators<Double> {
    override fun plus(a: Double, b: Double) = a + b
    override fun greaterThan(a: Double, b: Double) = a > b
}

fun <T> twiceTheLarger(a: T, b: T, operators: NumericOperators<T>) =
    if (operators.greaterThan(a, b)) operators.plus(a, a) else operators.plus(b, b)

fun main() {
    println(twiceTheLarger(1, 2, IntNumericOperators)) // int values
    println(twiceTheLarger(42.0, 1.0, DoubleNumericOperators)) // double values
}

Is there a better way?

Edit:

I realize that I could create overloads for each primitive numeric type. The thing is that I'm writing a library function that needs to work with arbitrary number-like types, even types my library doesn't know about. So type-specific overloads are not an option.

Upvotes: 4

Views: 495

Answers (1)

Nikola Despotoski
Nikola Despotoski

Reputation: 50578

You will need to create functions for each primitive type. You can't use <T : Number>, despite all numbers in Kotlin inherit this. Number superclass is used only for the castings.

You'll need to create functions or extension functions:

fun Int.twiceTheLarger(a: Int, b: Int) = if (a > b) a + a else b + b;

fun Double.twiceTheLarger(a: Double, b: Double) = if (a > b) a + a else b + b;

It would be great if you can utilize Comparable<T> in another function for the comparison. Types of T need to overload operator fun plus(other: T) as well.

    interface Addable<T>: Comparable<T>{
       operator fun <T> Addable<T>.plus(a: T)
    }
    fun <T : Addable<T>> T.twiceTheLarger(a: T, b: T) {
        return if (a > b) a.plus(a) else b + b
    }

Upvotes: 3

Related Questions