2017-12-19 3 views
1

ich einen Wrapper von Parcelable generischem Typ, aber Parcel Konstruieren nicht zu kompilieren, weil T Klasse nicht allgemein bestimmt werden kannGet Class Loader Generika für verschachtelte Parcelgenerische Feld Parsen

class MyItem<T : Parcelable> (val model: T) : Parcelable { 
    constructor(parcel: Parcel) : 
     this(parcel.readParcelable(T::class.java.classLoader)) { 

    } 
} 

Gibt es eine Lösung für diesen Fall?

+0

Keine Ahnung, ob es rel Ated, aber warum wird Ihr T "out" erklärt, wenn Sie es für den Konstruktor übergeben? Bedeutet das nicht, dass es nicht kovariant ist und daher nicht "out" sein sollte? Was passiert, wenn Sie "out" entfernen? –

+0

@PaulHicks wahr, aber es betrifft nicht das Problem –

+0

Yep dachte nicht so. Denken Sie daran, T ist keine Klasse, es ist ein formaler Parameter. Sie können eine [reified Inline-Funktion] (https://kotlinlang.org/docs/reference/inline-functions.html#reified-type-parameters) für diese Art von Sache verwenden, aber das wird in einem sekundären Konstruktor nicht funktionieren . Sie müssten eine normale verdinglichte Inline-Funktion in einer Factory oder wahrscheinlicher auf einem Companion-Objekt verwenden. –

Antwort

1

Um das ganze Bild hier zu bekommen ist, was ich am Ende mit:

Der Anwendungsfall ist man hat eine Parcelable generische Instanz es model nennen lassen, die mit gemeinsamen Eigenschaften von Parcelable Wrapper abgeschlossen sein soll um das Modell nicht mit extrinsischen Feldern zu belasten. Zum Beispiel Item Wrapper.

Im Beispiel unter dem Wrapper zusätzliche Eigenschaft gibt uns irgendeine Art von index:

class Item<T : Parcelable> (val model: T, val index: Int) : Parcelable { 

    constructor(parcel: Parcel) : 
     this(parcel.readParcelable(
      Item<T>::model.javaClass.classLoader), 
      parcel.readInt() 
     ) {} 

    override fun writeToParcel(parcel: Parcel?, flag: Int) { 
     parcel?.writeParcelable(model, 0) 
     parcel?.writeInt(index) 
    } 

    override fun describeContents(): Int { 
     return 0 
    } 

    companion object CREATOR : Parcelable.Creator<Item<Parcelable>> { 
     override fun createFromParcel(parcel: Parcel): Item<Parcelable> { 
      return Item(parcel) 
     } 

     override fun newArray(size: Int): Array<Item<Parcelable>?> { 
      return arrayOfNulls(size) 
     } 
    } 
} 

Also am Ende wir so etwas wie haben: Item(Person("John"), 0), Item(Person("Bill"), 1) ...

class PersonPagerFragment() : BasePagerFragment<Person>() { 
    companion object { 
     fun newInstance(itemList: ArrayList<Item<Person>>) 
      : PersonPagerFragment { 

      val args = Bundle() 
      val fragment = PersonPagerFragment() 
      args.putParcelableArrayList("items", itemList) 
      fragment.arguments = args 

      return fragment 
     } 
    } 
} 

Verlängerung Klasse wie:

class BasePagerFragment<T : Parcelable> : Fragment(){ 
    protected fun readBundle(bundle: Bundle?) { 
     bundle.getParcelableArrayList<Item<T>>("items")  
    } 
} 
+0

Das funktioniert. Sie nennen 'createFromParcel' oder' newArray' in keinem der Beispielcodes, wie werden diese verwendet? Das Builder-Muster wird oft für solche Dinge verwendet, und nicht für ein Modell-/Vorlagenobjekt. Verhindern externe Abhängigkeiten, dass Sie dieses Mal verwenden? –

+0

@PaulHicks Ich bin mir nicht sicher, ob ich deinen Gedanken genau folge, aber ich denke du meinst, dass ich die statischen Methoden nicht verwende?Die im Beispiel bereitgestellten Methoden sowie 'CREATOR' sind Mindestanforderungen, um' Parcelable' zu ​​erweitern. Meine Vermutung ist, 'putParcelableArrayList' und' getParcelableArrayList' oder 'putParcelable' und' getParcelable' Methoden verwenden diese. Leider wird die Quelle nicht auf die API heruntergeladen, die ich verwende :( –

+0

Ah, es gibt eine spezielle wohlbekannte 'Parcelable' API. Ich wusste das nicht. Es ist aber wahrscheinlich nicht wichtig für diese Frage. –

1

Sie können eine reified inline function als eine Fabrikmethode verwenden, um dies zu erreichen. Ich bevorzuge dies auf einem Begleitobjekt. Hier ist ein MCVE:

class MyItem<T> (val model: T) { 
    companion object { 
     inline fun <reified T> from(parcel : T) : MyItem<T> { 
      return MyItem<T>(T::class.java.newInstance()) 
     } 
    } 
} 
fun main(args: Array<String>) { 
    val mi = MyItem.from("hi") 
    println("mi is a ${mi::class}") 
} 
+0

Offensichtlich ein trivialer Fall hier, da ich Ihre 'readParcelable' Methode nicht habe. Hoffentlich ist es einfach, zu Ihrem genauen Fall zu übersetzen. –

+0

Das Problem in meinem Fall ist, dass ich keine Instanz von 'T' habe. Der 'Konstruktor (Parzelle: Parzelle)' wird automatisch aufgerufen :( –

+0

Sie müssen das ändern oder den primären Konstruktor ändern. –

1

Wenn Sie Notwendigkeit einen Parcel-Typkonstruktor haben, Sie könnten das ändern der primäre Konstruktor zu sein und herauszufinden, die Art des MyItem aus dann.

class Parcel 

class MyItem(val parcel: Parcel) { 
    inline fun <reified T> model() : T { 
     // You code would be calling 
     // `parcel.readParcelable(T::class.java.classLoader)` 
     return T::class.java.newInstance() as T 
    } 
} 
fun main(args: Array<String>) { 
    // You don't ned to know the out type when constructing MyItem. 
    val mi = MyItem(Parcel()) 

    // But you do need to know it when calling model() 
    val model : String = mi.model() 

    println("mi.model is a ${model::class}") 
} 
+0

Als ich String durch Parcel ersetzt habe, habe ich hier einige wichtige Probleme mit meiner ersten Antwort gefunden:) Versuchen Sie diese zweite Version. –

+0

10x für Ihre Hilfe war es sehr hilfreich –

Verwandte Themen