Hiranya Jayathilaka
Hiranya Jayathilaka

Reputation: 7438

Kotlin Type Parameters with Upper Bounds

I expect the following to produce a compilation error, but it doesn't. Any ideas why?

fun <T, U: T> foo(a: T, b: U) {
    println("$a $b")
}

foo("bar", 123)

If I call foo() with explicit type arguments, it fails to compile as expected:

foo<String, Int>("bar", 123)
Error: Kotlin: Type argument is not within its bounds: should be subtype of 'String'

Upvotes: 1

Views: 2019

Answers (3)

zsmb13
zsmb13

Reputation: 89558

The compiler tries quite hard to fit the parameters you've provided to a function signature. In this case, it infers the type parameters to be Any and Int, so it basically calls the function like this, which of course satisfies the upper bound requirement, because Any is a supertype of Int:

foo<Any, Int>("bar", 123)

You can check that this is indeed what's happening in at least two different ways.

  1. You can open intention actions (Alt + Enter on Windows, ⌥↩ on macOS) when your cursor is on the foo function call and choose Add explicit type arguments. This will produce the line above.

  2. You can make the function reified and print the type parameters' classes - these will be the parameters that it knows about at compile time.

    inline fun <reified T, reified U: T> foo(a: T, b: U) {
        println("$a $b")
        println(T::class) // class kotlin.Any
        println(U::class) // class kotlin.Int
    }
    

Upvotes: 5

John Perry
John Perry

Reputation: 2678

In the first case, the compiler has no idea what type you mean by T, so it has to guess, and it doesn't necessarily guess the strictest type. One assumption of languages that use type inference is that the programmer meant something correct. String and Int are both descendants of type Any, so the compiler can resolve the first call by substituting Any in place of T. Hence, there is no error.

Once you specifically specify types, the compiler has a better idea of what you want, and in that case it is an error. But the first case is correct.

Upvotes: 2

tynn
tynn

Reputation: 39853

The type inference tries to find a suitable pair. In this case it would be the explicit

foo<Any, Int>("bar", 123)

The compiler knows the relation of T and U and thus just needs to find the common super type for String and Int for T.

Upvotes: 4

Related Questions