Generic Object Creation


#1

What would be the Kotlin way to create objects of an unkown type. Given the following code:

interface Cell { ... }

class SomeCell : Cell { ... }

class CellList<T: Cell>(val num: Int) {
    private val data: MutableList<T> = ArrayList(num)

    init {
        for (i in 0..num - 1) {
            data[i] = ???  // how can I create an instance here?
        }
    }
}

I’d like to instantiate a number of CellLists that differ in their type of cell. How can I idiomatically achieve this in Kotlin?


#2

You need to pass a KClass<out Cell> as a constructor parameter to CellList, and to invoke the constructor using reflection. You can also create a factory function with a reified type parameter to enable creating CellList instances with a somewhat cleaner syntax.


#3

With Kotlin I prefer to pass along a factory function. This because it also gives you the freedom to use anything other then default constructors. Thus:

fun main(args: Array<String>) {
    val cl = CellList(10) {
        SomeCell()
    }
}

interface Cell { }

class SomeCell : Cell { }

class CellList<T: Cell>(val num: Int, factory : (Int) -> T) {
    private val cells: MutableList<T> = java.util.ArrayList(num)

    init {
        for (i in 0..num - 1) {
            cells.add(factory(i))
        }
    }
}

#4

I really want to avoid the reflection route. Can you explain what you mean with your second suggestion?

Sometimes it would be really nice, if interfaces could enforce certain constructors as well.


#5

You can use inline functions with reified type parameters to achieve something that closest to your initial question. You can go as berserk as:

fun main(args: Array<String>) {
    CellList(10) {
        SomeCell()
    }
    
    CellList<SomeCell>(10)
}

interface Cell { }

class SomeCell : Cell { }

class CellList<T: Cell>(val num: Int, factory : (Int) -> T) {
    companion object {
        inline operator fun <reified T: Cell> invoke(num : Int) = CellList(num) {
            T::class.java.newInstance()
        }
    }
    private val cells: MutableList<T> = java.util.ArrayList(num)
    
    init {
        for (i in 0..num - 1) {
            cells.add(factory(i))
        }
    }
}

#6

Thanks.


#7

This is super cool and does exactly what I want. Actually, it opens up a couple of new options. However, I’m having trouble finding the place where this is documented.

As I understand it, there’s some compiler magic going on that transforms CellList<SomeCell>(10) into CellList.invoke<SomeCell>(10).

That way you can add useful “constructors” to String:

operator fun String.Companion.invoke(repeatingChars: String, num: Int): String {
    val builder = StringBuilder()
    for (i in 0..num - 1) {
        builder.append(repeatingChars)
    }
    return builder.toString()
}

Unfortunately, that is not possible for Java classes that do not declare a companion object (like BigInteger). I’m wondering if that would be nice to be able to externally add those companion objects as well?

Something like

companion object BigInteger.Companion {
    ...
}

#8

There’s no magic here, just a “simple” operator overloading combined with reified inline functions and companion objects :wink:

Everything is in the docs: https://kotlinlang.org/docs/reference/operator-overloading.html

I second that idea. I’d like to be able to write extension functions for such objects (no companion objects need to be created in this case, it’s just about the syntax).


#9

How can I do this in Kotlin multiplatform?