Why this doesn't compile? I get compile error in 3 line
Cannot use T as reified type parameter. Use class instead
class Matrix2d<T>(val rows: Int, val cols: Int, init: (Int, Int) -> T) {
var data = Array(rows * cols, { i ->
val r = Math.floor(i.toDouble() / cols).toInt()
init(r, i - r * cols)
})
operator fun get(row: Int, col: Int): T = data[row * cols + col]
operator fun set(row: Int, col: Int, v: T) = {
data[row * cols + col] = v
}
}
Solution
I added a factory function which looks like a second constructor but implemented in inline function
class Matrix2d<T>(val rows: Int, val cols: Int, private val data: Array<T>) {
companion object {
operator inline fun <reified T> invoke(rows: Int, cols: Int, init: (Int, Int) -> T): Matrix2d<T> {
return Matrix2d(rows, cols, Array(rows * cols, { i ->
val r = Math.floor(i.toDouble() / cols).toInt()
init(r, i - r * cols)
}))
}
}
init {
if (rows * cols != data.size) throw IllegalArgumentException("Illegal array size: ${data.size}")
}
operator fun get(row: Int, col: Int): T = data[row * cols + col]
operator fun set(row: Int, col: Int, v: T) {
data[row * cols + col] = v
}
}
JVM arrays, on which Kotlin arrays are mapped to, require the element type to be known at compile time to create an instance of array.
So you can instantiate Array<String>
or Array<Any>
, but not Array<T>
where T
is a type parameter, representing the type that is erased at compile time and hence is unknown.
To specify that a type parameter must be known at compile time it is marked with reified
modifier.
There are several options, what you can do in this situation:
Use MutableList<T>
for storing elements, which doesn't require reified T:
// MutableList function, available in Kotlin 1.1
val data = MutableList(rows * cols, { i ->
val r = i / cols
init(r, i % cols)
})
// or in Kotlin 1.0
val data = mutableListOf<T>().apply {
repeat(rows * cols) { i ->
val r = i / cols
add(init(r, i % cols))
}
}
Create an array from an inline function with reified type parameter:
inline fun <reified T> Matrix2d(val rows: Int, val cols: Int, init: (Int, Int) -> T) =
Matrix2d(rows, cols, Array(rows * cols, { .... })
class Matrix2d<T>
@PublishedApi internal constructor(
val rows: Int, val cols: Int,
private val data: Array<T>
)
Use Array<Any?>
as the storage, and cast its values to T
in get
function:
val data = Array<Any?>(rows * cols, { .... })
operator fun get(row: Int, col: Int): T = data[row * cols + col] as T
Pass a parameter of type Class<T>
or KClass<T>
to constructor and use java reflection to create an instance of array.
Personally, the best workaround for me was:
@Suppress("UNCHECKED_CAST")
var pool: Array<T?> = arrayOfNulls<Any?>(initialCapacity) as Array<T?>