Restrict calling some functions of a class with Generic type parameters?

Is it possible to somehow restrict calling a method of a generic type to some type parameter constraints? Something like the (non working) example below?

class Foo<T>(val t: T) {
  // I can't do this because of
  // Type constraints are not allowed when no type parameters declared
  fun multiplyBy(other: Int): Int where T : Int {
    return (t as Int) * other
  }
}

Something else I tried:

class Foo<T>(val t: T) {
  // Type parameter cannot have any other bounds if it's bounded by another type parameter
  fun <U> multiplyBy(other: Int): Int where U: T, U : Int {
    return (t as Int) * other
  }
}

Something that works is using extension functions:

class Foo<T>(val t: T) {
}
// Yay, this works 🎉
fun <T:Int> Foo<T>.multiplyBy(other: Int): Int {
  return t *other
}

But this makes Java interop a bit akward…

I have the feeling there’s no silver bullet there but I’m curious if this problem has a name and if it could have a solution? (Maybe in newer versions of java?)

In the first case:

class Foo<T>(private val t: T) {

    // Let's pretend this would work
    fun multiplyBy(other: Int): Int where T : Int {
        return (t as Int) * other
    }
}

fun main() {
    val foo = Foo<String>("")
    val bar = foo.multiplyBy(2) // What this would be like?
}

I prefer to wrtie code like this:

class Bar<T : Number>(private val b: T) {

    // Unresolved reference. None of the following candidates is applicable because of receiver type mismatch:
    // public inline operator fun BigDecimal.times(other: BigDecimal): BigDecimal defined in kotlin
    // public inline operator fun BigInteger.times(other: BigInteger): BigInteger defined in kotlin
    //
    // Still won't work, but make sense: Number can multiply another Number
    fun <V> multiplyBy(other: V): T where V : T {
        return other * t
    }
}

What you’d like to do is only possible with extension methods — and for good.
Actually, that’s what OOP is for — you should use inheritance, instead, as follows:

open class FooWrapper<T>(val value: T)

abstract class NumberWrapper<T : Number>(value: T) : FooWrapper<T>(
    value = value
) {
    abstract fun multiplyBy(otherValue: Int): Number
}

class IntWrapper(value: Int) : NumberWrapper<Int>(
    value = value
) {
    override fun multiplyBy(otherValue: Int): Int {
        return value * otherValue;
    }
}

Note that you can create custom operators like this:

abstract operator fun times(otherValue: Int): Number

instead of

abstract fun multiplyBy(otherValue: Int): Number

And since you seem to care a lot about Java interoperability, you might want to purse with your naming:

@JvmName(name = "multiplyBy")
abstract operator fun times(otherValue: Int): Number

However, I don’t know what your goal is, but making wrappers like this looks awful and pointless. You may want to use extension functions altogether, as opposed to wrapping values to add features to them. Alternatively, you may at least opt for composition so to avoid replicating the features of a type in its wrapper, as long as either the wrapped type is immutable or its state does not affect the state of its wrapper.

Example extension function for Fibonacci series:

fun Int.fibonacci(): Long =
    fibonacci(n = this, a = 0, b = 1)

private tailrec fun fibonacci(n: Int, a: Long, b: Long): Long =
    when (n) {
        0 -> a
        else -> fibonacci(n = n - 1, a = b, b = a + b)
    }

/*
 * Usage:
 *     2.fibonacci() // to get the 2nd term of the Fibonacci series
 */

Composition:

class Wrapper<T>(val value: T)

fun Foo() {
    Wrapper(10).value * 50;
}

Further suggestion:
In case you don’t need inheritance and want to wrap a specific type, you will likely benefit from value classes, especially when it comes to primitive types (which would need to get boxed before being passed to generic parameter, as the latter’s lower boundary can’t be a primitive type itself).

@JvmInline
value class IntWrapper(val value: Int)

If you could possibly be more specific about your purpose, we all would be able to help you better.

1 Like

Hi @davidecannizzo !

We have a mutable Call<T> where T can be one of Query, or Subscription. Queries are one-shot returning only one value, Subscriptions are Flows. Returning multiple values.

Call is configurable and has multiple builder functions that return this:

class Call<T> {
  fun addHttpHeader(name: String, value: String): Call<T> = apply {
    // ...
  }

  // A lot of other common configuration options
}

Call can also execute and return one value or multiple values:

class Call<T> {
  suspend fun execute(): Response {
    //...
  }

  fun toFlow(): Flow<Response> {
    //...
  }
}

We can’t really make two different classes like QueryCall and SubscriptionCall because then we would have to duplicate all the Builder methods. Also there is some value in being able to manipulate a Call<*>.

But I’d still like to prevent users to call execute a Subscription as a one-shot and a Query as a Flow. This works with extension functions but it makes the java interop a bit awkward…

2 Likes

I don’t think Java really has a nice solution for this. However, Java devs are used to utility classes being thrown around, and so you can just have that extension function and at the top of the file do @file:JvmName("CallUtils")

Why? Wouldn’t it be enough to do something like this?

abstract class Call<TSelf : Call<TSelf, T>, T> {

     fun addHttpHeader(name: String, value: String): TSelf {
        TODO()
     }
}

class QueryCall<T> : Call<QueryCall<T>, T>() {
    // QueryCall specific methods
}

class SubscriptionCall<T> : Call<SubscriptionCall<T>, T>() {
    // SubscriptionCall specific methods
}

I’m not sure what do you mean by that — the solution I provided with self–referencing generics would allow you to maintain static type safety seamlessly.

1 Like

Indeed, that works :+1: . It’s still a bit of boilerplate because there was a copy() method involved too:

abstract class Call<TSelf : Call<TSelf, T>, T> {
     abstract fun copy(): TSelf
}

That’d need to be implemented twice.

Also for a user, the second type parameter adds cognitive load not to mention 3 classes instead of one. It’s all working but I was wondering if there was maybe another more concise way to look at this.

2 Likes

I hope that someone will figure out the solution you’re looking for, but I actually think there couldn’t be a more concise way. Kotlin uses single dispatch, which in type system theory means that the symbol (method) that’s going to be called is determined as a function of a single parameter only (the receiver). Since you’re defining methods as members (which you need to do for Java interoperability), the type of the receiver is the type of the class itself, with wildcards as generic arguments. So, while the methods you’d like to restrict are invoked directly as they’re not virtual, I believe it’d be pretty much incoherent from a language design standpoint to support such a thing, since it would need to be restricted to direct (i.e. non–virtual) methods only.

The additional type parameter is needed for inheritance only, so it is only be going to be used on subclasses referencing their superclass and references to whichever type of Calls (e.g. Call<*, *>). The latter issue may be worked around as follows:

typealias AnyCall<T> = Call<*, T>

So that you’d only need to reference calls of unknown types as:

AnyCall<Something>
// or
AnyCall<*>

I know this is not ideal, but at least it’s something. Hopefully something better will come up.

3 Likes