web-dev-qa-db-fra.com

Kotlin: Comment travailler avec des distributions de liste: Distribution non contrôlée: kotlin.collections.List <Kotlin.Any?> Vers kotlin.colletions.List <Waypoint>

Je veux écrire une fonction qui retourne chaque élément d'un List qui n'est ni le premier ni le dernier élément (un point intermédiaire). La fonction obtient un générique List<*> En entrée. Un résultat ne devrait être retourné que si les éléments de la liste sont du type 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}
}

Lors de la conversion du List<*> En List<Waypoint>, Je reçois l'avertissement:

Distribution non vérifiée: kotlin.collections.List dans kotlin.colletions.List

Je ne peux pas trouver un moyen de le mettre en œuvre autrement. Quelle est la bonne façon d'implémenter cette fonction sans cet avertissement?

69
lukle

Dans Kotlin, il n’existe aucun moyen de vérifier les paramètres génériques au moment de l’exécution (par exemple, il suffit de vérifier les éléments d’un List<T>, Ce qui n’est qu’un cas particulier), ce qui permet de convertir un type générique en un autre avec des paramètres génériques différents. émettra un avertissement à moins que la distribution ne soit comprise entre bornes de la variance .

Il existe cependant différentes solutions:

  • Vous avez vérifié le type et vous êtes sûr que le casting est sécurisé. Cela dit, vous pouvez supprimer l'avertissement avec @Suppress("UNCHECKED_CAST").

    @Suppress("UNCHECKED_CAST")
    val waypointList = list as? List<Waypoint> ?: return null
    
  • Utilisez la fonction .filterIsInstance<T>() , qui vérifie les types d'élément et renvoie une liste des éléments du type transmis:

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

    ou la même chose dans une déclaration:

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

    Cela créera une nouvelle liste du type souhaité (évitant ainsi une distribution non contrôlée à l'intérieur), introduisant un léger surcoût, mais en même temps, cela vous évite de parcourir le list et de vérifier les types (dans list.foreach { ... } En ligne), pour que cela ne soit pas perceptible.

  • Ecrivez une fonction utilitaire qui vérifie le type et renvoie la même liste si le type est correct, encapsulant ainsi la conversion (toujours non contrôlée du point de vue du compilateur):

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

    Avec l'utilisation:

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

En cas de classes génériques, les conversions ne peuvent pas être vérifiées car les informations de type sont effacées au moment de l'exécution. Mais vous vérifiez que tous les objets de la liste sont Waypoints afin que vous puissiez simplement supprimer l'avertissement avec @Suppress("UNCHECKED_CAST").

Pour éviter de tels avertissements, vous devez passer un List d'objets convertibles en Waypoint. Quand vous utilisez * mais en essayant d'accéder à cette liste sous forme de liste dactylographiée, vous aurez toujours besoin d'une distribution et cette distribution sera décochée.

3
Michael

Pour améliorer la réponse de @ hotkey voici ma solution:

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

Cela vous donne le List<Waypoint> si tous les éléments peuvent être convertis, null sinon.

1
Adam Kis

J'ai fait une petite variation à @hotkey answer quand je l'utilisais pour vérifier les objets Serializable to List:

    @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