2017-10-22 130 views
6

我有一個包含T和一些元數據的數組(或列表)的接口。Kotlin generics Array <T> results in「Can not use T as a reified type parameter。Use a class instead」但List <T> does not

interface DataWithMetadata<T> { 
    val someMetadata: Int 
    fun getData(): Array<T> 
} 

如果我寫的接口的最簡單的實現,我得到一個編譯錯誤的emptyArray():「不能使用T作爲一個具體化的類型參數使用類代替。」

class ArrayWithMetadata<T>(override val someMetadata: Int): DataWithMetadata<T> { 
    private var myData: Array<T> = emptyArray() 

    override fun getData(): Array<T> { 
     return myData 
    } 

    fun addData(moreData: Array<T>) { 
     this.myData += moreData 
    } 
} 

但是,如果我改變這兩個接口和實現到一個列表,我沒有編譯時的問題:

interface DataWithMetadata<T> { 
    val someMetadata: Int 
    fun getData(): List<T> 
} 

class ListWithMetadata<T>(override val someMetadata: Int): DataWithMetadata<T> { 
    private var myData: List<T> = emptyList() 

    override fun getData(): List<T> { 
     return myData 
    } 

    fun addData(moreData: Array<T>) { 
     this.myData += moreData 
    } 
} 

我懷疑有在科特林泛型一些有趣的課我的問題裏面。任何人都可以告訴我編譯器在底層做什麼,爲什麼Array失敗,但List不?在這種情況下是否有一種習慣方式來編譯數組實現?

獎金問題:我在List上列出的Array的唯一原因是我經常看到Kotlin開發人員喜歡陣列。是這樣,如果是這樣,爲什麼?

回答

5

綜觀emptyArray()在科特林STDLIB(JVM)的聲明中,我們注意到reified類型參數:

public inline fun <reified @PureReifiable T> emptyArray(): Array<T> 

reified類型參數意味着你必須在編譯時訪問類的T並可以像T::class那樣訪問它。您可以在Kotlin reference中閱讀關於reified類型參數的更多信息。由於Array<T>編譯爲java T[],因此我們需要知道編譯時的類型,因此需要了解reified參數。如果您嘗試沒有reified關鍵字寫一個emptyArray()函數,你會得到一個編譯器錯誤:

fun <T> emptyArray() : Array<T> = Array(0, { throw Exception() }) 

Cannot use T as a reified type parameter. Use a class instead.


現在,讓我們來看看在emptyList()實現:

public fun <T> emptyList(): List<T> = EmptyList 

該實現完全不需要參數T。它只是返回內部對象EmptyList,它本身繼承自List<Nothing>。 kotlin類型Nothing是關鍵字throw的返回類型,並且是從不存在的值reference)。如果某個方法返回Nothing,則等同於在該位置拋出異常。所以我們可以在這裏安全地使用Nothing,因爲每當我們調用EmptyList.get()時,編譯器知道這會返回一個異常。


獎金的問題:

從Java和C++的到來,我習慣了ArrayListstd::vector是更容易使用,數組。我現在使用kotlin幾個月,在編寫源代碼時,我通常看不到數組和列表之間的巨大差異。兩者都有許多有用的擴展函數,其行爲方式類似。但是,Kotlin編譯器處理數組和列表的方式非常不同,因爲Java互操作性對於Kotlin團隊非常重要。我通常更喜歡使用列表,這也是我的建議。

2

的問題是,一個Array必須在知道的通用元素類型的編譯時間,這是由reified類型參數指示在這裏,因爲在申報看到:

public inline fun <reified @PureReifiable T> emptyArray(): Array<T> 

這只是可能會創建像Array<String>Array<Int>但不是Array<T>類型的混凝土陣列。

在此answer中,您可以找到幾種解決方法。希望你找到一個合適的方法。

相關問題