2016-04-12 8 views
19

Voglio scrivere una funzione che restituisce ogni elemento in un List che non è il primo o l'ultimo elemento (un punto intermedio). La funzione ottiene come input un generico List<*>. Un risultato deve essere restituito solo se gli elementi della lista sono del tipo Waypoint:Kotlin: Come lavorare con cast di lista: Unchecked Cast: kotlin.collections.List <Kotlin.Any?> a kotlin.colletions.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} 
} 

Quando lancia il List<*> a List<Waypoint>, ottengo l'avvertimento:

incontrollato Cast: kotlin.collections. Elenco a kotlin.colletions.List

Non riesco a capire un modo per implementarlo altrimenti. Qual è il modo giusto per implementare questa funzione senza questo avviso?

risposta

52

In Kotlin, in generale non è possibile controllare i parametri generici in fase di esecuzione (ad esempio, è sufficiente controllare gli articoli di un List<T>, che è solo un caso speciale), quindi trasmettere un tipo generico a un altro con parametri generici diversi sollevare un avviso a meno che il cast si trovi all'interno di variance bounds.

ci sono diverse soluzioni, però:

  • Hai controllato il tipo e vi sono abbastanza sicuro che il cast è al sicuro. Detto questo, è possibile suppress the warning con @Suppress("UNCHECKED_CAST").

    @Suppress("UNCHECKED_CAST") 
    val waypointList = list as? List<Waypoint> ?: return null 
    
  • Usa .filterIsInstance<T>() funzione, che controlla i tipi di elemento e restituisce una lista con le voci del tipo passato:

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

    o la stessa in una dichiarazione:

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

    Questo creerà una nuova lista del tipo desiderato (evitando così il cast non controllato all'interno), introducendo un piccolo overhead, ma nello stesso tempo ti eviterà di iterare t tramite il list e controllando i tipi (nella riga list.foreach { ... }), quindi non sarà visibile.

  • Scrivi una funzione di utilità che controlla il tipo e restituisce alla medesima lista se il tipo è corretto, incapsulando così il cast (ancora incontrollato dal punto di vista del compilatore) al suo interno:

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

    Con il utilizzo:

    val waypointList = list.checkItemsAre<Waypoint>() ?: return null 
    
+2

grande risposta! Scelgo la soluzione list.filterIsInstance () perché penso che sia la soluzione più pulita. – lukle

+3

Si noti che se si utilizza ['filterIsInstance'] (https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/filter-is-instance.html) e l'elenco originale contiene elementi di un tipo diverso il tuo codice li filtrerà silenziosamente. A volte questo è quello che vuoi, ma a volte potresti preferire una "IllegalStateException" o una simile gettata. Se poi è il caso, puoi creare il tuo metodo per controllare e poi trasmettere: 'inline fun Iterable <*> .mapAsInstance() = map {it.apply {check (this is R)} come R}' – mfulton26

+0

Nota che '.apply' non restituisce il valore di ritorno di lambda, restituisce l'oggetto di ricezione. Probabilmente si vorrà usare '.takeIf' se si desidera che l'opzione restituisca un valore nullo. – bj0

2

In caso di classi generiche, i cast non possono essere controllati perché le informazioni sul tipo vengono cancellate in runtime. Ma controlli che tutti gli oggetti nell'elenco siano Waypoint s in modo da poter sopprimere l'avviso con @Suppress("UNCHECKED_CAST").

Per evitare tali avvisi, è necessario passare uno List di oggetti convertibili su Waypoint. Quando utilizzi * ma provi ad accedere a questo elenco come un elenco digitato, avrai sempre bisogno di un cast e questa trasmissione verrà deselezionata.

Problemi correlati