Momh
Momh

Reputation: 774

Kotlin - is it possible to infer generics type parameter?

Given this code:

// Can generic type parameter be inferred?
class Foo<T> {
    fun consume(p: T) {
        println("slurp $p")
    }
}

//region
abstract class ATestsOfFoo<U: Foo<T>, T>(private val foo: U) {

    fun test1() {
        foo.consume(someT())
    }

    abstract fun someT(): T
}

class TestsOfFoo(foo: Foo<String>): ATestsOfFoo<Foo<String>, String>(foo) {
    override fun someT(): String {
        return "Hello"
    }
}

val testsOfFoo = TestsOfFoo(Foo())
testsOfFoo.test1()
//endregion

I'm looking for a way to infer T type parameter from U.

Said differently, I'd like to be able to write the following line without repeating String as the type parameter:

...
// This is an example of what I'd like to be able to write, it does not work
class TestsOfFoo(foo: Foo<String>): ATestsOfFoo<Foo<String>>(foo) {
...

Is there a way to do so?

If not, is there a conceptual flaw that would forbid us to safely infer T from U?

P.S: I know we could simplify ATestsOfFoo like this:

abstract class ATestsOfFoo<T>(private val foo: Foo<T>) {
...

however I'm not looking for that, conceptually ATestsOfFoo operates on Foo<T>, I'd like to keep that information in type parameters.

Upvotes: 2

Views: 2195

Answers (1)

Mats Jun
Mats Jun

Reputation: 351

In short, no, Kotlin will not infer the type based on the super constructor call's argument types like this. Kotlin's type inference only supports local variable inference and function signature type inference.

You can read more about that in the Kotlin spec (https://kotlinlang.org/spec/type-inference.html)

I'm not sure if there's a conceptual flaw here and I am not that experienced in type systems, but I know this is inferable with TypeScript's type system but that's because TypeScript's constrait solver and type checker can work with conditional types and default types for generics, which is not a language feature Kotlin has.

interface Foo<T> {
  consume(p: T): void
}

interface FooImpl1<U extends Foo<T>, T = U extends Foo<infer X> ? X : never> {
  test(foo: U): T
}

class FooImpl implements FooImpl1<Foo<string>> {
  test(foo: Foo<string>): string { ... }
}

In conclusion; with the current type system Kotlin has we have no way to express this. Unfortunately you will just have to repeat the types.

Upvotes: 3

Related Questions