2016-01-30 14 views
8

Sto cercando di generalizzare il mio hack da una risposta a un altro question.Scrittura di una funzione util di Kotlin che fornisce riferimento automatico nell'inizializzatore

Dovrebbe fornire un modo per fare riferimento a un valore che non è ancora stato costruito all'interno del suo inizializzatore (ovviamente, non direttamente, ma in lambda e espressioni oggetto).

Quello che ho in questo momento:

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 
} 

Funziona, si veda questo esempio:

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 

Ma a questo punto il modo in cui initializer riferimenti al valore costruito è self proprietà.

E la mia domanda è: esiste un modo per riscrivere SelfReference in modo che initializer passi un argomento (o un destinatario) anziché utilizzare la proprietà self? Questa domanda può essere riformulata in: esiste un modo per passare un ricevitore/argomento valutato pigramente ad una funzione o raggiungere in qualche modo questa semantica?

Quali sono gli altri modi per migliorare il codice?


UPD: Un modo possibile è passare una funzione che restituisce self, quindi sarebbe utilizzato come it() all'interno initializer. Sto ancora cercando altri.

+1

Potrebbe essere possibile utilizzare una combinazione di [proprietà delegate] (https://kotlinlang.org/docs/reference/delegated-properties.html#delegated-properties), [proprietà late-inizializzate] (https://kotlinlang.org/docs/reference/properties.html#late-initialized-properties) e/o [funzioni inline] (https://kotlinlang.org/docs/reference/inline-functions.html#inline-functions) generalizzare questo.Il mio tentativo dovrebbe (credo) funzionare ma c'è un bug di compilazione Kotlin che impedisce di farlo ora (vedi https://youtrack.jetbrains.com/issue/KT-10878). – mfulton26

risposta

0

esiste un modo per riscrivere SelfReference in modo che all'inizializzatore venga passato un argomento (o un destinatario) anziché utilizzare la proprietà self? Questa domanda può essere riformulata in: esiste un modo per passare un ricevitore/argomento valutato pigramente ad una funzione o raggiungere in qualche modo questa semantica?

io non sono sicuro di aver capito completamente il vostro caso d'uso, ma questo potrebbe essere quello che stai cercando:

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 

Questo funziona perché holderAction è una lambda con un ricevitore (Holder.() -> Unit) dando la lambda accesso ai membri del destinatario.

Questa è una soluzione generale poiché potrebbe non essere possibile modificare la firma del rispettivo costruttore Holder. Può valere la pena notare che questa soluzione non richiede che la classe sia aperta, altrimenti un approccio simile potrebbe essere fatto con una sottoclasse usando un costruttore secondario.

Preferisco questa soluzione per creare una classe SelfReference quando ci sono solo poche classi che richiedono la modifica.

È possibile controllare null invece di utilizzare !! per generare un errore utile. Se Holder chiama action nel suo costruttore o blocco di init, otterrai un'eccezione di puntatore nullo.

Problemi correlati