2017-01-30 2 views
3

Warum kompiliert das nicht? Ich bekomme Kompilierungsfehler in 3 ZeileGenerisches Array in Kotlin instanziieren

Kann nicht T als verdingter Typ Parameter verwenden. Verwenden Klasse statt

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

Lösung

Ich habe eine Fabrik-Funktion, die wie eine zweite Konstruktor sieht aber

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 
    } 
} 
+3

Mögliches Duplikat [Kotlin NDArray mit einem Lambda-Konstruktor mit allgemeinem Rückgabetyp] (http://stackoverflow.com/questions/35581867/kotlin-ndarray-with-a-lambda-constructor-with-generic- Rückgabetyp) – mfulton26

Antwort

2

JVM-Arrays in Inline-Funktion implementiert, auf denen Kotlin Arrays mapped to erfordert, dass der Elementtyp zum Zeitpunkt der Kompilierung bekannt ist, um eine Array-Instanz zu erstellen.

So können Sie Array<String> oder Array<Any>, instanziiert aber nicht Array<T> wo T ein Typparameter ist, die die Art, die zum Zeitpunkt der Kompilierung wird gelöscht und damit ist nicht bekannt. Um festzulegen, dass ein Typparameter zum Zeitpunkt der Kompilierung bekannt sein muss, wird er mit reified Modifikator markiert.

Es gibt mehrere Optionen, was können Sie in dieser Situation tun:

  1. Verwendung MutableList<T> zur Speicherung von Elementen, die nicht verdinglicht T benötigt:

    // 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. Erstellen Sie ein Array aus eine Inline-Funktion mit einem bestätigten Typ-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. Verwenden Array<Any?> als Speicher und warf seine Werte in Tget Funktion:

    val data = Array<Any?>(rows * cols, { .... }) 
    
    operator fun get(row: Int, col: Int): T = data[row * cols + col] as T 
    
  4. Geben einen Parameter vom Typ Class<T> oder KClass<T> Konstrukteur und Java Reflexion zu verwenden, um eine Instanz des Arrays zu schaffen.

+0

Danke für die Erklärung, ich mag den zweiten Weg. Ich habe meinen Beitrag aktualisiert und der Code funktioniert ordnungsgemäß, aber jetzt habe ich einen öffentlichen Konstruktor in der Matrix2d-Klasse, der von allen verwendet werden kann, und er ist verwirrend, weil ich einen dritten Parameter als Array habe. Und wenn ich diesen Konstruktor verwende, sollte ich die Anzahl der Zeilen und Spalten übergeben. Ich wollte den Contructor-Bereich auf "Privat" festlegen, aber er ist nicht für die Inline-Funktion verfügbar. Wie kann ich dieses Problem auf andere Weise lösen? – Lancaster

+1

Verwenden Sie den internen Konstruktor. Um ihn jedoch über die öffentliche Inline-Funktion aufzurufen, müssen Sie ihn mit der Annotation @PublishedApi (verfügbar in Kotlin 1.1) versehen. Ich werde das Beispiel aktualisieren. – Ilya