Hi,
sorry for asking a maybe stupid question but I aim fairly new to kotlin. I have this code which is accepted by IntelliJ (by the way version 2018.1.3 with kotlin 1.2.41-release-IJ2018.1-1)
val data = mutableListOf<XYChart.Data<LocalDate,Number>>()
P.eep.forEach {data.add(XYChart.Data(it.startDate,it.rate))}
where eep is declared as List
What I originally wanted to write is
val data = mutableListOf<XYChart.Data<LocalDate,Number>>()
data.addAll(P.eep.map {XYChart.Data(it.startDate,it.rate)})
but this is not accepted. The error is
Error:(178, 49) Kotlin: Type inference failed. Expected type mismatch: inferred type is List<XYChart.Data<LocalDate!, Double!>> but Collection<XYChart.Data<LocalDate, Number>> was expected
and I cannot understand why because for me adding all the elements or each one by one looks to be the same. Can you please enlighten me?
Thank you very much,
claudio
1 Like
Hi,
here a short self contained kotlin program showing the issue:
package ch.claudio.stuff
import javafx.scene.chart.XYChart
import java.time.LocalDate
data class RP(val startDate:LocalDate,val rate:Double)
fun main(args: Array<String>) {
val eep=listOf(RP(LocalDate.of(2018, 5, 24), 1.0))
val data=mutableListOf<XYChart.Data<LocalDate,Number>>()
eep.forEach {data.add(XYChart.Data(it.startDate,it.rate))}
data.addAll(eep.map {XYChart.Data(it.startDate,it.rate)})
}
claudio
Please use β```β at the beginning and end of code you post. That way it gets properly formatted.
So the problem is kind of complex. It has to do with how Kotlin handles Nullability in combination with Java types.
Normally a type can either be nullable ( Foo?
) or not ( Foo
). But there is a third option, which gets used for values returned by java code which can be either ( Foo!
).
You define data to be of type MutableList<Data<LocalDate, Number>>
. When you try to call addAll
it expects an argument of type Collection<Data<LocalDate, Number>>
but instead you pass a list of type Collection<Data<LocalDate!, Number!>>
. That is the reason it fails.
To solve this you could use a cast it explicitly
data.addAll(eep.map {
@Suppress("UNCHECKED_CAST")
XYChart.Data(it.startDate, it.rate) as XYChart.Data<LocalDate, Number>
})
So now the only question is why can you call add
with an argument of type Data<LocalDate!, Number!
but not add all? I guess this is due to the way the compiler handles generics.
This also does not work.
eep.forEach { data.add(XYChart.Data(it.startDate, it.rate)) } // this works
eep.forEach {
val temp= XYChart.Data(it.startDate, it.rate)
data.add(temp) // this fails
}
In all I think this qualifies as a bug. And even if not the handling of Generics with unspecified nullability seems like it could be improved.
You can specify the Data type so it generates a XYChart.Data<LocalDate, Number>
instead of XYChart.Data<LocalDate!, Number!>
:
data.addAll(eep.map { XYChart.Data<LocalDate, Number>(it.startDate, it.rate) })
Woops yeah, definitely better than the cast I suggested. This does still not explain why addAll
does not work the same way that add
does.
I think add
works because it receives a specific type, so the compiler infer the type from java to be XYChart.Data<LocalDate, Number>
.
But with addAll
the map
function is transforming it from java first and now it is XYChart.Data<LocalDate!, Number!>
, then it is used as parameter for the addAll
which canβt accept the, now specifc, type(with !).
Hi,
thank you all for the solution and the explanation.
claudio