2016-04-12 24 views
19

Chcę napisać funkcję, która zwraca każdy element w List że nie jest to pierwsza lub ostatnia pozycja (punkt przelotowy). Funkcja otrzymuje ogólne wejście List<*>. Wynik powinien być zwrócony tylko wtedy, gdy elementy listy są typu Waypoint:Kotlin: Jak pracować z listy rzuca: niezaznaczone Występują: kotlin.collections.List <Kotlin.Any?> do 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} 
} 

Podczas odlewania List<*> do List<Waypoint>, pojawia się ostrzeżenie:

niezaznaczone Obsada: kotlin.collections. Listę do kotlin.colletions.List

Nie mogę wymyślić sposób realizacji go inaczej. Jaki jest właściwy sposób wdrożenia tej funkcji bez tego ostrzeżenia?

Odpowiedz

52

W Kotlinie nie ma sposobu na sprawdzenie ogólnych parametrów w czasie wykonywania w ogólnym przypadku (np. Po prostu sprawdzenie pozycji List<T>, co jest tylko specjalnym przypadkiem), więc rzutowanie rodzaju ogólnego na inny o różnych parametrach ogólnych będzie podnieść ostrzeżenie, chyba że obsada znajduje się w zakresie variance bounds.

Istnieją różne rozwiązania, jednakże:

  • Sprawdzono rodzaj i jesteś pewien, że obsada jest bezpieczny. Biorąc to pod uwagę, można suppress the warning z @Suppress("UNCHECKED_CAST").

    @Suppress("UNCHECKED_CAST") 
    val waypointList = list as? List<Waypoint> ?: return null 
    
  • Zastosowanie .filterIsInstance<T>() funkcja, która sprawdza typy elementów i zwraca listę z elementów przekazany typu:

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

    lub takie same w jednej instrukcji:

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

    Spowoduje to utworzenie nowej listy pożądanego typu (dzięki czemu unikniesz niezaznaczonej obsady wewnątrz), wprowadzając trochę narzut, ale jednocześnie oszczędza ci to iteracji przez list i sprawdzanie typów (w linii list.foreach { ... }), więc nie będzie to zauważalne.

  • Napisz funkcję użytkową, który sprawdza typ i zwraca samej listy, jeśli typ jest prawidłowy, więc enkapsulacji obsady (jeszcze niesprawdzony z punktu kompilator widzenia) wewnątrz niej:

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

    Z wykorzystanie:

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

Świetna odpowiedź! Wybieram rozwiązanie list.filterIsInstance (), ponieważ uważam, że jest to najczystsze rozwiązanie. – lukle

+3

Należy zauważyć, że jeśli używasz ['filterIsInstance'] (https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/filter-is-instance.html), a oryginalna lista zawiera elementy innego typu Twój kod będzie je cicho odfiltrowywać. Czasem to jest to, czego chcesz, ale czasami wolisz mieć wyjątek "IllegalStateException" lub podobny. Jeśli jest to późniejsze, możesz utworzyć własną metodę sprawdzania, a następnie rzutowania: 'inline fun Iterable <*> .mapAsInstance() = map {it.apply {check (to jest R)} jako R}' – mfulton26

+0

Pamiętaj, że '.apply' nie zwraca zwracanej wartości lambda, zwraca obiekt receive. Prawdopodobnie chcesz użyć '.takeIf', jeśli chcesz, aby opcja zwróciła wartość null. – bj0

2

W przypadku klas ogólnych nie można sprawdzić, czy informacje o typie są usuwane w czasie wykonywania. Ale sprawdzasz, czy wszystkie obiekty na liście są Waypoint s, więc możesz po prostu wyłączyć ostrzeżenie za pomocą @Suppress("UNCHECKED_CAST").

Aby uniknąć takich ostrzeżeń, należy przekazać List obiektów przekształcalnych na Waypoint. Gdy używasz *, ale próbujesz uzyskać dostęp do tej listy jako listy maszynowej, zawsze będziesz potrzebować rzutowania, a ta odlewność nie będzie zaznaczona.