Tの配列(またはリスト)といくつかのメタデータを含むインターフェイスがあります。
_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
}
}
_
私の問題の中には、Kotlinジェネリックに関する興味深い教訓があると思います。誰もコンパイラが内部で何をしているか、なぜアレイは失敗するがリストは失敗しないのか教えてもらえますか?このコンテキストでArray実装をコンパイルする慣用的な方法はありますか?
おまけの質問:リストよりも配列にたどり着いた唯一の理由は、Kotlin開発者が配列を好むのをよく見かけるからです。これは事実ですか?もしそうなら、なぜですか?
Kotlin stdlib(jvm)のemptyArray()
の宣言を見ると、reified
typeパラメーターに注目してください。
_public inline fun <reified @PureReifiable T> emptyArray(): Array<T>
_
reified
型パラメーターは、コンパイル時にT
のクラスにアクセスでき、_T::class
_のようにアクセスできることを意味します。 Kotlinリファレンス でreified
型パラメーターの詳細を読むことができます。 _Array<T>
_はJava _T[]
_)にコンパイルされるため、コンパイル時に型、つまりreified
パラメーターを知る必要があります。 reified
キーワードを指定しないemptyArray()関数を使用すると、コンパイラエラーが発生します。
_fun <T> emptyArray() : Array<T> = Array(0, { throw Exception() })
_
Tを具体化された型パラメーターとして使用することはできません。代わりにクラスを使用してください。
それでは、emptyList()
の実装を見てみましょう。
_public fun <T> emptyList(): List<T> = EmptyList
_
この実装では、パラメータT
はまったく必要ありません。内部オブジェクトEmptyList
を返すだけで、それ自体は_List<Nothing>
_から継承します。 kotlin型Nothing
は、throw
キーワードの戻り値の型であり、決して存在しない値です( reference =)。メソッドがNothing
を返す場合、その場所で例外をスローすることと同じです。したがって、ここでNothing
を安全に使用できます。なぜなら、EmptyList.get()
を呼び出すたびに、コンパイラは例外が返されることを知っているからです。
ボーナス質問:
JavaおよびC++)から来て、私はArrayList
または_std::vector
_に慣れており、その配列をはるかに使いやすくしています。通常、ソースコードの作成時に配列とリストの間に大きな違いは見られません。どちらも同様の動作をする便利な拡張関数がたくさんあります。ただし、KotlinコンパイラはJava相互運用性は、Kotlinチームにとって非常に重要です。
問題は、Array
のジェネリック型がcompile timeで認識されなければならないことです。これはreified
型で示されます宣言で見られるように、ここのパラメーター:
public inline fun <reified @PureReifiable T> emptyArray(): Array<T>
Array<String>
やArray<Int>
などの具体的な配列のみを作成できますが、Array<T>
型は作成できません。
この answer には、いくつかの回避策があります。適切な方法を見つけてください。
私にとって最適な回避策は次のとおりです。
@Suppress("UNCHECKED_CAST")
var pool: Array<T?> = arrayOfNulls<Any?>(initialCapacity) as Array<T?>