Rob Elliot
10/01/2023, 9:37 PMfoo(vararg elements: Bar)
and foo(elements: List<Bar>)
because sometimes it's convenient to use varargs, but sometimes I have a List and *elements.toTypedArray()
is a bit of a faff.
I tend just to make one delegate to the other - but I just encountered a case where it was pure waste to allocate the extra List or Array, as all I then did was iterate over it.
Is there any way to abstract over Array
and List
to treat both as the same sort of thing?fun foo(vararg elements: Bar) {
for (element in elements) {
callMethodThatUses(element)
}
}
fun foo(elements: List<Bar>) {
for (element in elements) {
callMethodThatUses(element)
}
}
It would be nice to have:
fun foo(vararg elements: Bar) = doFoo(elements)
fun foo(elements: List<Bar>) = doFoo(elements)
private fun doFoo(elements: ???) {
for (element in elements) {
callMethodThatUses(element)
}
}
Ben Woodworth
10/01/2023, 9:59 PMRob Elliot
10/01/2023, 10:01 PMYoussef Shoaib [MOD]
10/02/2023, 3:05 AMabstract class ListLike<in L, out E> {
abstract operator fun L.get(index: Int): E
abstract val L.size: Int
final inline fun L.forEach(block: (E) -> Unit) { for(i in 0..size) block(i) }
object ListImpl: ListLike<List<*>, Any?>() {
override operator fun List<*>.get(index: Int) = this[index]
override val List<*>.size = size
}
object ArrayImpl: ListLike<Array<*>, Any?>() {
override operator fun Array<*>.get(index: Int) = this[index]
override val Array<*>.size = size
}
}
(Code untested because I'm on mobile)
Simply just bring in the apt XImpl instance using with
, and the function with the real implementation should have ListLike<L, E>
as its receiver (ideally a context receiver if you're on the bleeding edge), or it can just take it as a parameter and bring it in itself. You might need some unsafe casts here or there, which can simply be wrapped in a function on ListLike's companion.ephemient
10/02/2023, 3:18 AMList
version, and make the callers write foo([1, 2, 3])
varargs
might not be backed by arrays anymore, and then perhaps the alternative will not require wrapping into a ListStephan Schröder
10/02/2023, 7:00 AMephemient
10/02/2023, 7:08 AMarrayOf<Number>() as Array<Int> // throws ClassCastException
arrayOf<Int>() as Array<Number> // succeeds as if it were covariant, even though a mutable container should really be invariant
(arrayOf(1) as Array<Number>)[0] = 1.0 // throws ArrayStoreException
this is different than how all modern generics work, because of JVM behavior pre-dating Java 1.0Stephan Schröder
10/02/2023, 7:54 AMvarargs
a List instead of an Array? Was an Array chosen for historical reasons or for efficiency or is there another reason??
@Rob Elliot so List
and Array
don't have a joint interface, but both can produce an Iterator<T>
, so you could use that as a unifying solution!?! You won't have a for-loop
(that one works on Iterable<T>
) but you do have forEach
and map
.CLOVIS
10/02/2023, 7:57 AMWas an Array chosen for historical reasonsI believe it was for interoperability with Java's varargs, which are arrays ; so you can call Java vararg functions from Kotlin and the reverse transparently
Stephan Schröder
10/02/2023, 7:58 AMKlitos Kyriacou
10/03/2023, 11:15 AMList
and Array
don't have a joint interface, but both can produce an Iterator<T>
, so you could use that as a unifying solution!?! You won't have a for-loop
(that one works on Iterable<T>
)
Funnily enough, the for
loop works on Iterators too, even though the docs say it needs an Iterable:
https://pl.kotl.in/3f7a7Dh0_
fun main() {
for (c in arrayOf('a', 'b', 'c').iterator())
println(c)
}
iterator()
function!Youssef Shoaib [MOD]
10/03/2023, 11:17 AM