web-dev-qa-db-ja.com

KotlinジェネリックArray <T>は、「Tを具体化された型パラメーターとして使用できません。代わりにクラスを使用してください」という結果になりますが、List <T>はそうではありません。

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開発者が配列を好むのをよく見かけるからです。これは事実ですか?もしそうなら、なぜですか?

29
Nate Vaughan

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チームにとって非常に重要です。

21
msrd0

問題は、Arrayのジェネリック型がcompile timeで認識されなければならないことです。これはreified型で示されます宣言で見られるように、ここのパラメーター:

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

Array<String>Array<Int>などの具体的な配列のみを作成できますが、Array<T>型は作成できません。

この answer には、いくつかの回避策があります。適切な方法を見つけてください。

7
s1m0nw1

私にとって最適な回避策は次のとおりです。

@Suppress("UNCHECKED_CAST")
var pool: Array<T?> = arrayOfNulls<Any?>(initialCapacity) as Array<T?>
1
Riki137