2

EDIT March 1 2016: Fair warning: this question was asked about Kotlin before 1.0.0. Things are different since Kotlin 1.0.0. See @Jayson Minard's writing below for a Kotlin 1.0.0 answer.

In Java 8 code that uses Stream, I write things like

public static void main(String... args) {
    Stream<Integer> integerStream = Stream.of(1,2,3);
    List<Integer> integerList = integerStream.collect(Collectors.toList());
}

But in similar code written in Kotlin, I get unexpected results.

public fun main(args: Array<String>) {
    val integerStream : Stream<Int> = Stream.of(1, 2, 3)

    // this is what I expect to write, like the Java 8 code, but is a compilation error: 
    // "required: java.util.List<in kotlin.Int> found: kotlin.MutableList<in kotlin.Int!"
    // val list : java.util.List<in Int> = integerStream.collect(Collectors.toList()) 

    // I must instead write this
    val list : MutableList<in Int> = integerStream.collect(Collectors.toList())
}

Why would the return value of the Stream#collect expression be of a different list type in the Kotlin code than in the Java code? (I'm guessing it is because of some Java Collections-specific magic in Kotlin)

David Groomes
  • 2,303
  • 1
  • 21
  • 23
  • See also: http://stackoverflow.com/a/35722167/3679676 – Jayson Minard Mar 01 '16 at 12:54
  • 1
    In Kotlin 1.0 you should be able to just set the type as `List` and it will be fine. But your `toList()` call will need to be `toList` due to a bug mentioned in the link in the comment above. – Jayson Minard Mar 01 '16 at 12:56
  • your comment at the top ... you can change accepted answer. The idea of StackOverflow is to help new people reading the posts, not historical accuracy. Things should always be updated to current. Thanks for the note at the top, it'll help users in the meantime! – Jayson Minard Mar 02 '16 at 22:16
  • Begs the question; do all questions and answers get audited for correctness against the current version of the language/software/framework or library they were written about? Very Interesting. – Gavin Mar 04 '16 at 22:46

2 Answers2

3

I think it is because List in Java are mutable be default, where as in Kotlin they are immutable. In the code you are using a Java specific utility, Collectors.toList which returns a Java List, which to Kotlin translates as a MutableList, so in effect you are writing the same code, just the type names are different.

Gavin
  • 1,725
  • 21
  • 34
  • So, in other (more provocative) words, "Kotlin prohibits Java Collection types"? I'm very curious in learning more specifics. – David Groomes Nov 15 '15 at 01:49
  • Potentially so. I would have to read the docs to find out. (I haven't tried Kotlin/Java inter-op yet) – Gavin Nov 15 '15 at 01:55
  • 2
    Silly me, it says right in the "Java Interop" section of the docs that some types are automatically mapped between Java and Kotlin. See https://kotlinlang.org/docs/reference/java-interop.html#mapped-types – David Groomes Nov 15 '15 at 02:01
1

You can do this in Kotlin 1.0:

val integerStream : Stream<Int> = Stream.of(1, 2, 3)
val list : List<Int> = integerStream.collect(Collectors.toList<Int>())

or

val integerStream : Stream<Int> = Stream.of(1, 2, 3)
val list : MutableList<Int> = integerStream.collect(Collectors.toList<Int>())

or not care, and let inference decide:

val integerStream = Stream.of(1, 2, 3)
val list = integerStream.collect(Collectors.toList<Int>())

NOTE: I changed your toList() call into toList<Int>() to work around the issue mentioned in https://stackoverflow.com/a/35722167/3679676

You can also create an extension function:

fun <T: Any> Stream<T>.toList(): List<T> = this.collect(Collectors.toList<T>())

And then use it for any Stream<T> to List<T> conversion:

val integerStream = Stream.of(1, 2, 3)
val list = integerStream.toList()

val stringStream = Stream.of("a", "b", "c")
val stringList = stringStream.toList()

And this last example is already returning the read only interface of List instead of MutableList. For the difference between these and other mapped interfaces in Kotlin, see the docs for Java Interop, mapped types.

Lastly, one other variant is to convert to a Sequence and stay lazy, since it is the Kotlin equivalent of a Stream.

fun <T: Any> Stream<T>.asSequence(): Sequence<T> = this.iterator().asSequence()

Or Stream.iterator() is similar, but not the same.

Community
  • 1
  • 1
Jayson Minard
  • 84,842
  • 38
  • 184
  • 227