25

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
   }
}
1

2 Answers 2

29

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:

  1. 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))
        }
    }
    
  2. 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>
        ) 
    
  3. 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
    
  4. Pass a parameter of type Class<T> or KClass<T> to constructor and use java reflection to create an instance of array.

Sign up to request clarification or add additional context in comments.

2 Comments

Thanks for explanation, I like the second way. I updated my post and code works properly, but now I have a public constructor in Matrix2d class which can be used by all, and it's confusing, because I have a third parameter as an array. And if I use that constructor I should pass the number of rows and columns. I wanted to specify contructor scope to private, but it's not available for inline function. How can I solve this problem in a different way?
Use internal constructor, though to call it from public inline function, you have to annotate it with @PublishedApi annotation (available in Kotlin 1.1). I'll update the example.
5

Personally, the best workaround for me was:

@Suppress("UNCHECKED_CAST")
var pool: Array<T?> = arrayOfNulls<Any?>(initialCapacity) as Array<T?>

2 Comments

This solution is just as viable as solution 3 in the selected answer; just probably should have private access.
I don't think this works, it throws a ClassCastException with a misleading error message (see stackoverflow.com/questions/54536895/…) but the fact still remains that it cannot do the cast

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.