在Kotlin中创建通用的2D数组

时间:2021-04-24 21:18:24

Suppose I have a generic class and I need a 2D array of generic type T. If I try the following

假设我有一个泛型类,我需要一个通用类型为T的2D数组。如果我尝试以下

class Matrix<T>(width: Int, height: Int) {
    val data: Array<Array<T>> = Array(width, arrayOfNulls<T>(height))
}

the compiler will throw an error saying "Cannot use 'T' as reified type parameter. Use a class instead.".

编译器将抛出错误,说“不能使用'T'作为具体类型参数。请改用类。”

2 个解决方案

#1


8  

Just because the syntax has moved on a bit, here's my take on it:

仅仅因为语法已经移动了一下,这是我对它的看法:

class Array2D<T> (val xSize: Int, val ySize: Int, val array: Array<Array<T>>) {

    companion object {

        inline operator fun <reified T> invoke() = Array2D(0, 0, Array(0, { emptyArray<T>() }))

        inline operator fun <reified T> invoke(xWidth: Int, yWidth: Int) =
            Array2D(xWidth, yWidth, Array(xWidth, { arrayOfNulls<T>(yWidth) }))

        inline operator fun <reified T> invoke(xWidth: Int, yWidth: Int, operator: (Int, Int) -> (T)): Array2D<T> {
            val array = Array(xWidth, {
                val x = it
                Array(yWidth, {operator(x, it)})})
            return Array2D(xWidth, yWidth, array)
        }
    }

    operator fun get(x: Int, y: Int): T {
        return array[x][y]
    }

    operator fun set(x: Int, y: Int, t: T) {
        array[x][y] = t
    }

    inline fun forEach(operation: (T) -> Unit) {
        array.forEach { it.forEach { operation.invoke(it) } }
    }

    inline fun forEachIndexed(operation: (x: Int, y: Int, T) -> Unit) {
        array.forEachIndexed { x, p -> p.forEachIndexed { y, t -> operation.invoke(x, y, t) } }
    }
}

This also allows you to create 2d arrays in a similar manner to 1d arrays, e.g. something like

这也允许您以与1d数组类似的方式创建2d数组,例如就像是

val array2D = Array2D<String>(5, 5) { x, y -> "$x $y" }

and access/set contents with the indexing operator:

使用索引操作符访问/设置内容:

val xy = array2D[1, 2]

#2


8  

The problem is calling arrayOfNulls<T>(height) with the non-reified type parameter T. But we also can't make T reified, the compiler will throw the following error: "Only type parameters of inline functions can be reified"

问题是使用非reified类型参数T调用arrayOfNulls (height)但是我们也无法使用T,编译器将抛出以下错误:“只能内联函数的类型参数”

So that's what we're going to do. Instead of the constructor we use an inlined factory method:

这就是我们要做的。我们使用内联工厂方法而不是构造函数:

class Matrix<T> private(width: Int, height: Int, arrayFactory: (Int) -> Array<T>) {

    class object {
        inline fun <reified T>invoke(width: Int, height: Int)
                = Matrix(width, height, { size -> arrayOfNulls<T>(size) })
    }

    val data: Array<Array<T>> = Array(width, { size -> arrayFactory(size) })
}

Notice, the constructor is now private, so calling Matrix() will correctly call the new invoke() method (related question). Because the method is inlined, we can use reified generics which makes it possible to call arrayOfNulls<T>.

注意,构造函数现在是私有的,因此调用Matrix()将正确调用新的invoke()方法(相关问题)。因为该方法是内联的,所以我们可以使用reified泛型来调用arrayOfNulls

#1


8  

Just because the syntax has moved on a bit, here's my take on it:

仅仅因为语法已经移动了一下,这是我对它的看法:

class Array2D<T> (val xSize: Int, val ySize: Int, val array: Array<Array<T>>) {

    companion object {

        inline operator fun <reified T> invoke() = Array2D(0, 0, Array(0, { emptyArray<T>() }))

        inline operator fun <reified T> invoke(xWidth: Int, yWidth: Int) =
            Array2D(xWidth, yWidth, Array(xWidth, { arrayOfNulls<T>(yWidth) }))

        inline operator fun <reified T> invoke(xWidth: Int, yWidth: Int, operator: (Int, Int) -> (T)): Array2D<T> {
            val array = Array(xWidth, {
                val x = it
                Array(yWidth, {operator(x, it)})})
            return Array2D(xWidth, yWidth, array)
        }
    }

    operator fun get(x: Int, y: Int): T {
        return array[x][y]
    }

    operator fun set(x: Int, y: Int, t: T) {
        array[x][y] = t
    }

    inline fun forEach(operation: (T) -> Unit) {
        array.forEach { it.forEach { operation.invoke(it) } }
    }

    inline fun forEachIndexed(operation: (x: Int, y: Int, T) -> Unit) {
        array.forEachIndexed { x, p -> p.forEachIndexed { y, t -> operation.invoke(x, y, t) } }
    }
}

This also allows you to create 2d arrays in a similar manner to 1d arrays, e.g. something like

这也允许您以与1d数组类似的方式创建2d数组,例如就像是

val array2D = Array2D<String>(5, 5) { x, y -> "$x $y" }

and access/set contents with the indexing operator:

使用索引操作符访问/设置内容:

val xy = array2D[1, 2]

#2


8  

The problem is calling arrayOfNulls<T>(height) with the non-reified type parameter T. But we also can't make T reified, the compiler will throw the following error: "Only type parameters of inline functions can be reified"

问题是使用非reified类型参数T调用arrayOfNulls (height)但是我们也无法使用T,编译器将抛出以下错误:“只能内联函数的类型参数”

So that's what we're going to do. Instead of the constructor we use an inlined factory method:

这就是我们要做的。我们使用内联工厂方法而不是构造函数:

class Matrix<T> private(width: Int, height: Int, arrayFactory: (Int) -> Array<T>) {

    class object {
        inline fun <reified T>invoke(width: Int, height: Int)
                = Matrix(width, height, { size -> arrayOfNulls<T>(size) })
    }

    val data: Array<Array<T>> = Array(width, { size -> arrayFactory(size) })
}

Notice, the constructor is now private, so calling Matrix() will correctly call the new invoke() method (related question). Because the method is inlined, we can use reified generics which makes it possible to call arrayOfNulls<T>.

注意,构造函数现在是私有的,因此调用Matrix()将正确调用新的invoke()方法(相关问题)。因为该方法是内联的,所以我们可以使用reified泛型来调用arrayOfNulls