Since value classes (aka inline classes) are not classes at runtime but the value class, is it possible to make a list of them, and the list is bound to the list of values?
If i couldn't explain that very clearly, here is a code example:
class Foo(var i)
@JvmInline
value class FooWrapper(val foo: Foo)
fun main() {
val fooList = mutableListOf(Foo(1), Foo(2))
val wrappedFooList = fooList.???<FooWrapper>()
// fooList and wrappedFooList are the same list at runtime, so when you insert a value to fooList, it gets "added" to wrappedFooList as `FooWrapper(added)`
// This is what im currently using, but this is a seperate list, so when a value gets inserted into fooList, it doesn't get inserted here.
val wrappedFooListButNotTheSameList = fooList.map { FooWrapper(it) }
fooList.add(Foo(3)) // FooWrapper(Foo(3)) now exists in wrappedFooList
}
CodePudding user response:
Since value classes (aka inline classes) are not classes at runtime but the value class
This is only true sometimes. Inlining does not always happen, and the class file for the inline class does exist.
As soon as you start using generics, inlining goes out of the window. That is, your list of FooWrapper
would not be inlined at all.
However, sometimes it is necessary to keep wrappers around. As a rule of thumb, inline classes are boxed whenever they are used as another type.
See also the code sample that follows that. This is likely because when they are used as another type, code that doesn't know about the inline class is likely going to be interacting with the wrapper, and unexpected behaviours would occur if they are not boxed.
With all that in mind, if you still want two lists of unrelated types, that are "linked" together, you can first encode the conversion between the types with an interface:
interface ConvertibleTo<T> {
val converted: T
}
data class Foo(var i: Int): ConvertibleTo<FooWrapper> {
override val converted get() = FooWrapper(this)
}
@JvmInline
value class FooWrapper(val foo: Foo): ConvertibleTo<Foo> {
override val converted get() = foo
}
Then make a ConvertList<T, U>
and a ConvertListIterator<T, U>
by delegating everything (yes this is a lot of boilerplate). The built-in by
can't help here because you are also adding an extra .converted
on every U
value. Instead of the interfaces, you can also add T.() -> U
and U.() -> T
in the constructor parameters.
class ConvertList<T: ConvertibleTo<U>, U: ConvertibleTo<T>>(private val list: MutableList<T>): MutableList<U> {
override val size: Int
get() = list.size
override fun contains(element: U) = list.contains(element.converted)
override fun containsAll(elements: Collection<U>) =
list.containsAll(elements.map(ConvertibleTo<T>::converted))
override fun get(index: Int) =
list[index].converted
override fun indexOf(element: U) =
list.indexOf(element.converted)
override fun isEmpty() = list.isEmpty()
override fun iterator() = ConvertListIterator(list.listIterator())
override fun lastIndexOf(element: U) = list.lastIndexOf(element.converted)
override fun add(element: U) = list.add(element.converted)
override fun add(index: Int, element: U) = list.add(index, element.converted)
override fun addAll(index: Int, elements: Collection<U>) =
list.addAll(index, elements.map(ConvertibleTo<T>::converted))
override fun addAll(elements: Collection<U>) =
list.addAll(elements.map(ConvertibleTo<T>::converted))
override fun clear() = list.clear()
override fun listIterator() = ConvertListIterator(list.listIterator())
override fun listIterator(index: Int) = ConvertListIterator(list.listIterator(index))
override fun remove(element: U) = list.remove(element.converted)
override fun removeAll(elements: Collection<U>) =
list.removeAll(elements.map(ConvertibleTo<T>::converted))
override fun removeAt(index: Int) = list.removeAt(index).converted
override fun retainAll(elements: Collection<U>) =
list.retainAll(elements.map(ConvertibleTo<T>::converted))
override fun set(index: Int, element: U) = list.set(index, element.converted).converted
override fun subList(fromIndex: Int, toIndex: Int) = ConvertList(list.subList(fromIndex, toIndex))
}
class ConvertListIterator<T: ConvertibleTo<U>, U: ConvertibleTo<T>>(private val iter: MutableListIterator<T>): MutableListIterator<U> {
override fun hasPrevious() = iter.hasPrevious()
override fun nextIndex() = iter.nextIndex()
override fun previous() = iter.previous().converted
override fun previousIndex() = iter.previousIndex()
override fun add(element: U) = iter.add(element.converted)
override fun hasNext() = iter.hasNext()
override fun next() = iter.next().converted
override fun remove() = iter.remove()
override fun set(element: U) = iter.set(element.converted)
}
Usage:
val list = mutableListOf(Foo(1), Foo(2))
val list2 = ConvertList(list)