0

Minimal example:

fun <T> Iterable<T>.find2(elem: T) = this.find { it == elem }

Here, T is used to both denote the type of the Iterable, as well as the type of elem. It seems fine, however, a statement like this is syntactically correct, but doesn't make semantic sense:

listOf(1, 2, 3).find2("foo")

I assume this works because T resolves to Any.

I'm aware of the solution to explicitly state the type of the function:

listOf(1, 2, 3).find2<Int>("foo")

In this case, the compiler reports an incompatibility error, and rightfully so. However, this doesn't seem to be the solution, since it requires to explicitly declare the type, and will not report an error if forgotten (which makes bugs likely).

Is there a way to "constrict" the type <T>, so that, if, for example, the receiver is of type Iterable<Int>, the parameter must also be Int? In other words, is there a way to prevent the implicit cast to Any?

Aleksandar Stefanović
  • 1,583
  • 2
  • 20
  • 36
  • Here's the issue: in Kotlin, an `Iterable` is _also_, always, an `Iterable`. – Louis Wasserman Sep 29 '21 at 22:50
  • 1
    It's not an implicit cast to `Any`, it's that `T` is resolved to whatever type is an ancestor of the `List` contents and the argument you pass. And this is something which very often one needs, imagine having a list containing some interface type and you looking for a concrete instance. You want `T` to be the common ancestor of the types involved. – al3c Sep 30 '21 at 12:20

1 Answers1

0

In the statement

listOf(1, 2, 3).find2("foo")

There are actually two types that need to be resolved, T1 in listOf<T1> and T2 in Iterable<T2>.find2, and have the following constraints

  • T1 is the same as T2, because listOf returns List<T1>, which is Iterable<T2>, for find2
  • 1, 2 and 3 should be assignable to T1
  • "foo" should be assignable to T2

Therefore, the type has to be Any.

We usually explicitly declare the type of the list since it is the source,

listOf<Int>(1, 2, 3).find2("foo")

Or separate it into two statements

val listOfInt = listOf(1, 2, 3)
listOfInt.find2("foo")
James Lan
  • 248
  • 2
  • 8