2015-02-16 19 views
9

Giả sử tôi có một lớp chung và tôi cần một mảng 2D loại chung T. Nếu tôi thử như sauTạo mảng 2D chung trong Kotlin

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

trình biên dịch sẽ ném ra một lỗi nói rằng "Không thể sử dụng 'T' là tham số kiểu reified. Sử dụng một lớp học để thay thế.".

Trả lời

7

Chỉ vì cú pháp đã chuyển sang một chút, đây là quan điểm của tôi về nó:

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

này cũng cho phép bạn tạo ra các mảng 2ngày trong một cách tương tự như mảng 1d, ví dụ cái gì đó như

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

và truy cập/bộ nội dung với các nhà điều hành lập chỉ mục:

val xy = array2D[1, 2] 
8

Vấn đề đang kêu gọi arrayOfNulls<T>(height) với tham số kiểu T. phi reified Nhưng chúng ta cũng không thể làm cho T reified, trình biên dịch sẽ ném các lỗi sau: "tham số kiểu Chỉ các chức năng nội tuyến có thể được reified"

Vì vậy, đó là những gì chúng tôi sẽ làm. Thay vì các nhà xây dựng, chúng tôi sử dụng một phương pháp nhà máy inlined:

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

Thông báo, các nhà xây dựng hiện riêng tư, vì vậy gọi Matrix() một cách chính xác sẽ gọi phương thức invoke mới() (related question). Vì phương pháp được gạch chân, chúng tôi có thể sử dụng reified generics giúp bạn có thể gọi số arrayOfNulls<T>.