web-dev-qa-db-ja.com

Kotlin:リストキャストの使用方法:チェックなしキャスト:kotlin.collections.List <Kotlin.Any?> to kotlin.colletions.List <Waypoint>

最初のアイテムでも最後のアイテムでもないListのすべてのアイテムを返す関数を作成したい(経由点)。この関数は、一般的なList<*>を入力として取得します。リストの要素がWaypoint型である場合にのみ、結果が返されます。

fun getViaPoints(list: List<*>): List<Waypoint>? {

    list.forEach { if(it !is Waypoint ) return null }

    val waypointList = list as? List<Waypoint> ?: return null

    return waypointList.filter{ waypointList.indexOf(it) != 0 && waypointList.indexOf(it) != waypointList.lastIndex}
}

List<*>List<Waypoint>にキャストすると、次の警告が表示されます。

未チェックのキャスト:kotlin.collections.Listからkotlin.colletions.List

そうでなければ、それを実装する方法がわかりません。この警告なしでこの機能を実装する正しい方法は何ですか?

69
lukle

Kotlinでは、一般的な場合(特別な場合のみであるList<T>の項目をチェックするなど)実行時にジェネリックパラメーターをチェックする方法がないため、ジェネリックタイプを別のジェネリックパラメーターを持つ別のキャストにキャストすると、警告が発生しますキャストは variance bounds 内にあります。

ただし、さまざまなソリューションがあります。

  • タイプを確認し、キャストが安全であると確信しています。その場合、@Suppress("UNCHECKED_CAST")警告を抑制 できます。

    @Suppress("UNCHECKED_CAST")
    val waypointList = list as? List<Waypoint> ?: return null
    
  • .filterIsInstance<T>() 関数を使用します。これは、アイテムタイプをチェックし、渡されたタイプのアイテムのリストを返します。

    val waypointList: List<Waypoint> = list.filterIsInstance<Waypoint>()
    
    if (waypointList.size != list.size)
        return null
    

    または1つのステートメントで同じ:

    val waypointList = list.filterIsInstance<Waypoint>()
        .apply { if (size != list.size) return null }
    

    これにより、目的の型の新しいリストが作成され(内部で未チェックのキャストが回避されます)、少しオーバーヘッドが発生しますが、同時にlistを繰り返し処理して(list.foreach { ... }行で)型をチェックする必要がなくなります。目立たないでしょう。

  • 型をチェックし、型が正しい場合は同じリストを返すユーティリティ関数を作成し、キャストをカプセル化します(コンパイラーの観点からはまだチェックされていません):

    @Suppress("UNCHECKED_CAST")
    inline fun <reified T : Any> List<*>.checkItemsAre() =
            if (all { it is T })
                this as List<T>
            else null
    

    使用法:

    val waypointList = list.checkItemsAre<Waypoint>() ?: return null
    
137
hotkey

ジェネリッククラスの場合、型情報は実行時に消去されるため、キャストをチェックできません。ただし、リスト内のすべてのオブジェクトがWaypointsであることを確認するため、@Suppress("UNCHECKED_CAST")を使用して警告を抑制することができます。

このような警告を回避するには、Listに変換可能なオブジェクトのWaypointを渡す必要があります。 *を使用しているが、このリストに型付きリストとしてアクセスしようとすると、常にキャストが必要になり、このキャストはオフになります。

3
Michael

@hotkeyの答えを改善するために、ここに私の解決策があります:

val waypointList = list.filterIsInstance<Waypoint>().takeIf { it.size == list.size }

これにより、すべてのアイテムをキャストできる場合はList<Waypoint>、そうでない場合はnullが得られます。

1
Adam Kis

Serializable to Listオブジェクトのチェックに使用する場合、@ hotkey answerに少し変化を加えました。

    @Suppress("UNCHECKED_CAST")
    inline fun <reified T : Any> Serializable.checkSerializableIsListOf() =
        if (this is List<*> && this.all { it is T })
          this as List<T>
        else null
0
Samiami Jankis