Kotlin Use-Site Projections
A use-site projection puts out or in on one parameter type at one call shape. The class itself stays invariant, and only that use gains the wider or narrower view.
Reading an array through out
printAll takes an Array of out Any. It can read an Array of String and an Array of Int through that one parameter.
fun printAll(items: Array<out Any>) {
for (item in items) {
println(item)
}
}
fun main() {
printAll(arrayOf("one", "two"))
printAll(arrayOf(1, 2))
}Writing a list through in
addGreetings takes a MutableList of in String. The same function writes into a MutableList of Any and a MutableList of String.
fun addGreetings(target: MutableList<in String>) {
target.add("hello")
target.add("world")
}
fun main() {
val anyList = mutableListOf<Any>(1, true)
addGreetings(anyList)
println(anyList)
val stringList = mutableListOf<String>()
addGreetings(stringList)
println(stringList)
}Summing numbers through out
total takes a MutableList of out Number and reads each element as a Number. It sums an Int list and a Double list with the same code.
fun total(numbers: MutableList<out Number>): Double {
var sum = 0.0
for (n in numbers) {
sum += n.toDouble()
}
return sum
}
fun main() {
println(total(mutableListOf(1, 2, 3)))
println(total(mutableListOf(1.5, 2.5)))
}Example
fun printAll(items: Array<out Any>) {
for (item in items) {
println(item)
}
}
fun addTag(target: MutableList<in String>) {
target.add("tagged")
}
fun total(numbers: MutableList<out Number>): Double {
var sum = 0.0
for (n in numbers) {
sum += n.toDouble()
}
return sum
}
fun main() {
printAll(arrayOf("red", "blue"))
val bag = mutableListOf<Any>(7)
addTag(bag)
println(bag)
println(total(mutableListOf(10, 20)))
println(total(mutableListOf(0.5, 1.5)))
}Key points
- Array is invariant, so an Array of String is not an Array of Any on its own.
- A parameter typed Array of out Any can accept an Array of String for reading.
- A parameter typed MutableList of in String can accept a MutableList of Any for writing.
- A parameter typed MutableList of out Number can read Int and Double lists as numbers.
- The projection binds only that parameter, not the class.
In this example, three functions each carry one projection. printAll reads an Array of String through out Any, addTag writes a String into a MutableList of Any through in String, and total reads two number lists through out Number.
Note
A projection is written on the use site, in the function signature that needs it.
Try it Yourself
Edit the code, then press Run.
Running is simulated · Kotlin Playground
Exercise
Which parameter type lets a function write Strings into a MutableList of Any?