Kotlin: Liste yayınlarıyla nasıl çalışılır: Denetlenmemiş Oyuncular: kotlin.collections.List <Kotlin.Any?> Kotlin.colletions.List <Waypoint>


109

Listİlk veya son öğe olmayan (bir geçiş noktası) içindeki her öğeyi döndüren bir işlev yazmak istiyorum . İşlev List<*>, girdi olarak bir genel alır . Bir sonuç, yalnızca listenin öğeleri şu türdeyse döndürülmelidir 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}
}

Oyuncu kadrosu oluşturulurken List<*>için List<Waypoint>, ben uyarı alıyorum:

Kontrolsüz Oyuncular: kotlin.collections.List to kotlin.colletions.List

Aksi takdirde uygulamanın bir yolunu bulamıyorum. Bu uyarı olmadan bu işlevi uygulamanın doğru yolu nedir?

Yanıtlar:


196

Kotlin'de, genel olarak çalışma zamanında genel parametreleri kontrol etmenin bir yolu yoktur ( List<T>yalnızca özel bir durum olan a öğesinin öğelerini kontrol etmek gibi ), bu nedenle genel bir türün diğerine farklı jenerik parametrelerle çevrilmesi bir uyarı oluşturmaz. döküm varyans sınırları içinde yer alır .

Ancak farklı çözümler var:

  • Türü kontrol ettiniz ve alıcının güvenli olduğundan oldukça eminsiniz. Bu göz önüne alındığında, ile uyarıyı bastırabilirsiniz@Suppress("UNCHECKED_CAST") .

    @Suppress("UNCHECKED_CAST")
    val waypointList = list as? List<Waypoint> ?: return null
  • .filterIsInstance<T>()Öğe türlerini kontrol eden ve geçilen türdeki öğelerle bir liste döndüren kullanım işlevi:

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

    veya bir ifadede aynı:

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

    Bu, istenen türden yeni bir liste oluşturacak (böylece içerideki kontrolsüz dökümden kaçınarak) biraz ek yük getirecek, ancak aynı zamanda sizi yinelemekten listve türleri ( list.foreach { ... }sıralı) kontrol etmekten kurtaracak , böylece olmayacak farkedilebilir.

  • Türü kontrol eden ve tür doğruysa aynı listeyi döndüren bir yardımcı program işlevi yazın;

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

    Kullanım ile:

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

6
Mükemmel cevap! List.filterIsInstance <Waypoint> () çözümünü seçiyorum çünkü bunun en temiz çözüm olduğunu düşünüyorum.
Lukas Lechner

4
Kullanırsanız filterIsInstanceve orijinal liste farklı türde öğeler içeriyorsa, kodunuzun bunları sessizce filtreleyeceğini unutmayın. Bazen istediğiniz şey budur, ancak bazen bir IllegalStateExceptionveya benzerini atmayı tercih edebilirsiniz. Daha sonra durum söz konusuysa, kontrol etmek ve ardından inline fun <reified R> Iterable<*>.mapAsInstance() = map { it.apply { check(this is R) } as R }
yayınlamak

3
Not .applylambda dönüş değeri geri dönmez, bu nesneyi almak döndürür. .takeIfBir boş döndürme seçeneğinin olmasını istiyorsanız, muhtemelen kullanmak istersiniz.
bj0

10

@ Hotkey'in yanıtını iyileştirmek için işte benim çözümüm:

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

Bu size List<Waypoint>tüm öğelerin dökülebileceğini, aksi takdirde null değerini verir .


3

Genel sınıflar söz konusu olduğunda, tür bilgileri çalışma zamanında silindiğinden, yayınlar kontrol edilemez. Ancak listedeki tüm nesnelerin Waypoints olduğunu kontrol edersiniz, böylece ile uyarıyı bastırabilirsiniz @Suppress("UNCHECKED_CAST").

Bu tür uyarılardan kaçınmak için List, dönüştürülebilen nesneler arasında geçiş yapmanız gerekir Waypoint. *Bu listeyi kullanarak ancak yazılı bir liste olarak erişmeye çalıştığınızda, her zaman bir kadroya ihtiyacınız olacak ve bu kadro işaretlenmeyecek.


1

Nesneleri Listelemek için Serializable'ı kontrol etmek için kullanıldığında @hotkey cevabına küçük bir değişiklik yaptım:

    @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

Bunun gibi bir çözüm arıyordum, ancak bu hatalar:Cannot access 'Serializable': it is internal in 'kotlin.io'
daviscodesbugs

0

Onun yerine

myGenericList.filter { it is AbstractRobotTurn } as List<AbstractRobotTurn>

yapmayı seviyorum

myGenericList.filter { it is AbstractRobotTurn }.map { it as AbstractRobotTurn }

Bunun ne kadar başarılı olduğundan emin değilim, ama en azından uyarı yok.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.