0

I have the following function:

fun bar(list: MutableList<in Person>) {
    for(a in list) {
        println(a.getName())
    }
}

The problem is that a is considered of type Any and I would need to cast to Person first. Is this how it is supposed to be, or am I doing something wrong?
In Java if I declare with extends Person I would not need to cast.
I.e. if I had:

public <T extends Person> void bar(List<T> list)
Jim
  • 3,845
  • 3
  • 22
  • 47

2 Answers2

4

The Java method signature you gave is for a generic method (i.e. one that binds a type variable). This allows you to specify that your list must have that element type, allowing you to both retrieve and insert elements into the list safely, knowing they're of that type. You can do this in Kotlin too:

public <T extends Person> void bar(List<T> list);

is equivalent to

fun <T: Person> bar(list: MutableList<T>)

However, the Kotlin method signature you gave in your question is not generic, but has a wildcard generic argument. You can do this in Java too:

fun bar(list: MutableList<in Person>)

is equivalent to

public void bar(List<? super Person> list);

This is different to the generic method signature above. In the generic method, we can create variables of type T by taking elements out of the list, and we know that these are a subtype of Person. Since list has been bound to have element type T we also know that we can insert such variables back into the list.

With the non-generic, wildcard signature, we can't do that. In both the Java and Kotlin cases we have a method that takes any list whose element type is a supertype of Person. So, when we get elements out of the list, all we know for certain is that they are a supertype of Person, and the only type that fits this condition is Object/Any. However, the constraint on the wildcard does tell us that it should be fine to insert elements of type Person (or a subtype) into the list.

Just for completeness, the converse is also possible:

public void bar(List<? extends Person> list);

is equivalent to

fun bar(list: MutableList<out Person>)

Here, the method can take any list whose element type is a subtype of Person. We know that if we extract an element from the list, it will conform to the Person interface, so we can assign it to a variable of type Person. However, we don't know anything about what types we can insert into the list, as we don't know the exact subtype of Person the list accepts. Even though we know that a value we've just extracted from the list must be an acceptable type for insertion, I believe neither Kotlin nor Java is clever enough to infer this. You have to use the generic method signature as a "hint".

user31601
  • 2,482
  • 1
  • 12
  • 22
3

in and out are concepts not known to Java, cf. What is out keyword in kotlin, Understanding one usage of “in” keyword in Kotlin

The proper replacement for your Java code would be:

fun <T : Person> bar(list: MutableList<T>) {
    for(a in list) {
        println(a.getName())
    }
}

(thanks to user31601 for pointing that out in the comments)

Marvin
  • 13,325
  • 3
  • 51
  • 57
  • That won't suit me because I need the `list` to be modifiable. In the Java snippet I mentioned I am able to do both – Jim Jul 26 '21 at 09:26
  • Then use `MutableList`. – user31601 Jul 26 '21 at 09:27
  • @user31601: wont work either because I would need to be able to pass lists of different types of `Person`. As I said the Java equivalent works exactly as I require – Jim Jul 26 '21 at 09:28
  • 2
    The java version works because in Java you have a generic method, whereas in Kotlin you don't. So probabably what your looking for is `fun bar(list: MutableList)`. – user31601 Jul 26 '21 at 09:30
  • Sorry, I also got confused about the usage of `in` and `out`. – Marvin Jul 26 '21 at 09:38
  • The problem is that the caller does not even compile now. It works in Java. Seems I can not pass a generic subtype to the function – Jim Jul 26 '21 at 09:51
  • 1
    Please provide a [MCVE] then, so we don't have to guess what your actual code looks like. – Marvin Jul 26 '21 at 09:55