2016-01-30 5 views
8

Ich versuche, meinen Hack von einer Antwort auf eine andere question zu verallgemeinern.Eine Kotlin-Util-Funktion schreiben, die eine Selbstreferenz im Initialisierer liefert

Es sollte eine Möglichkeit bieten, einen Wert zu referenzieren, der noch nicht in seinem Initialisierer konstruiert wurde (natürlich nicht direkt, aber in lambdas und Objektausdrücken).

Was ich im Moment haben:

class SelfReference<T>(val initializer: SelfReference<T>.() -> T) { 
    val self: T by lazy { 
     inner ?: throw IllegalStateException("Do not use `self` until initialized.") 
    } 

    private val inner = initializer() 
} 

fun <T> selfReference(initializer: SelfReference<T>.() -> T): T { 
    return SelfReference(initializer).self 
} 

Es funktioniert, sehen Sie folgendes Beispiel:

class Holder(var x: Int = 0, 
      val action:() -> Unit) 

val h: Holder = selfReference { Holder(0) { self.x++ } } 
h.action() 
h.action() 
println(h.x) //2 

Aber an diesem Punkt die Art und Weise, in der initializer verweist auf den rechnerisch ermittelten Wert ist self Eigenschaft.

Und meine Frage ist: Gibt es eine Möglichkeit SelfReference neu zu schreiben, so dass initializer ein Argument übergeben (oder einen Empfänger) anstelle self Eigenschaft zu verwenden? Diese Frage kann neu formuliert werden: Gibt es eine Möglichkeit, einen träge bewerteten Empfänger/Argument einer Funktion zu übergeben oder diese Semantik irgendwie zu erreichen?

Was sind die anderen Möglichkeiten, den Code zu verbessern?


UPD: Eine Möglichkeit ist es, eine Funktion zu übergeben, die self zurückkehrt, so würde es als it() innerhalb der initializer verwendet werden. Suche immer noch nach anderen.

+1

Möglicherweise können Sie eine Kombination aus [delegierten Eigenschaften] (https://kotlinglang.org/docs/reference/delegated-properties.html#delegated-properties), [spät initialisierten Eigenschaften] (https://kotlinlang.org/docs/reference/properties.html#late-initialized-properties) und/oder [Inline-Funktionen] (https://kotlinlang.org/docs/reference/inline-functions.html#inline-functions) um dies zu verallgemeinern.Mein Versuch sollte (glaube ich) funktionieren, aber es gibt einen Kotlin-Kompilierungs-Bug, der das jetzt verhindert (siehe https://youtrack.jetbrains.com/issue/KT-10878). – mfulton26

Antwort

0

Gibt es eine Möglichkeit, SelfReference neu zu schreiben, so dass initializer ein Argument (oder ein Empfänger) übergeben wird, anstatt selbst zu verwenden? Diese Frage kann neu formuliert werden: Gibt es eine Möglichkeit, einen träge bewerteten Empfänger/Argument einer Funktion zu übergeben oder diese Semantik irgendwie zu erreichen?

Ich bin nicht sicher, ich völlig Ihren Anwendungsfall verstehen, aber dies kann sein, was Sie suchen:

fun initHolder(x: Int = 0, holderAction: Holder.() -> Unit) : Holder { 
    var h: Holder? = null 
    h = Holder(x) { h!!.holderAction() } 
    return h 
} 

val h: Holder = initHolder(0) { x++ } 
h.action() 
h.action() 
println(h.x) // 2 

Dies funktioniert, weil holderAction eine Lambda mit einem Empfänger (Holder.() -> Unit) gibt die Lambda-Zugang zu den Mitgliedern des Empfängers.

Dies ist eine allgemeine Lösung, da Sie möglicherweise die Signatur des jeweiligen Holder Konstruktors nicht ändern können. Es mag erwähnenswert sein, dass diese Lösung nicht erfordert, dass die Klasse offen ist, andernfalls könnte ein ähnlicher Ansatz mit einer Unterklasse unter Verwendung eines sekundären Konstruktors durchgeführt werden.

Ich bevorzuge diese Lösung zum Erstellen einer SelfReference Klasse, wenn es nur wenige Klassen gibt, die die Änderung benötigen.

Sie können nach null anstelle von !! suchen, um einen hilfreichen Fehler zu werfen. Wenn Holder in seinem Konstruktor- oder Init-Block action aufruft, erhalten Sie eine Nullzeiger-Ausnahme.

Verwandte Themen