4
votes

Instancier une variable dans Kotlin uniquement s'il s'agit d'un null?

Disons que j'ai une variable:

var myObject: MyObject? = null

il devrait être effacé à un endroit:

private fun getMyObject() = myObject ?: MyObject()

et devrait être définitivement non nullable dans un lieu d'utilisation. En Java, je peux faire quelque chose comme ceci:

private MyObject getMyObject(){
  if(myObject == null) {
    myObject = new MyObject()
  }
  return myObject
}

La question: Comment puis-je y parvenir dans Kotlin?

I a trouvé une suggestion pour utiliser elvis-operator:

myObject?.clear
myObject = null

mais cela n'attribue pas de résultat (si une nouvelle instance de MyObject serait créée) au Variable myObject . Veuillez m'aider avec la solution et l'explication. merci d'avance


2 commentaires

Vous pouvez faire la même chose comme en Java: private fun getMyObject (): MyObject {if (myObject == null) {myObject = MyObject ()} return myObject}


@Sergey - bien sûr, je comprends que je peux utiliser cette approche. la question porte sur la solution la plus élégante


5 Réponses :


2
votes

Vous pouvez combiner l'approche de type Java avec l'utilisation de l'opérateur Elvis, en écrivant quelque chose comme ceci:

private fun getMyObject() : MyObject {
    myObject = myObject ?: MyObject()
    return myObject as MyObject
}

La conversion explicite en MyObject est nécessaire en raison de Déclaration de myObject : var myObject MyObject? = nul . myObject est un nullable MyObject mais le type de retour de getObject est MyObject . J'espère que cela pourra vous aider!


0 commentaires

5
votes

Vous pouvez utiliser le champ de sauvegarde de la propriété.

pre> XXX

Pour l'essayer:

class Foo(initBar: String? = null) {

    private val barDelegate = NullDelegate(initBar) { "Automatically set" }
    var bar: String by barDelegate // not nullable to outside world

    fun clearBar() {
        barDelegate.clear()
    }

}

// Reusable. Not thread-safe.
class NullDelegate<T>(
    private var value: T? = null, 
    private val supplier: () -> T
) {

    operator fun getValue(thisRef: Any?, property: KProperty<*>): T {
        if (value == null) value = supplier()
        return value!!
    }

    operator fun setValue(thisRef: Any?, property: KProperty<*>, value: T?) {
        this.value = value
    }

    fun clear() {
        value = null
    }

}

Malheureusement, la propriété doit être nullable pour que cela fonctionne. Vous devrez utiliser !! ou ?. partout.


Vous pouvez également utiliser un délégué. Cela prend plus de code pour écrire la propriété mais facilite son utilisation ailleurs.

fun main() {
    val foo = Foo()
    foo.bar = "Manually set"
    println(foo.bar)
    foo.bar = null
    println(foo.bar)
}

Pour définir bar sur null code> vous appelleriez foo.clearBar () au lieu de foo.bar = null .


1 commentaires

Excellent moyen d'utiliser les délégués!



2
votes

Une version plus courte de la version de @ Slaw est la suivante:

var myObject: Any? = null
    get() = field = field ?: Any() // Does not compile

Cela instanciera un nouveau MyObject dans le champ de sauvegarde si la propriété est null sur un accès get .


Informations supplémentaires: strong>

Vous penseriez probablement à une version plus courte similaire à la suivante

var myObject: MyObject? = null
    get() {
        field = field ?: MyObject()
        return field
    }

Veuillez garder à l'esprit que cela ne compile pas em> avec le message d'erreur: Les attributions ne sont pas des expressions, et seules les expressions sont autorisées dans ce contexte .


0 commentaires

9
votes

Le problème est que le getter et le setter d'une propriété ne peuvent pas avoir des types différents. Je suggérerais une propriété privée Nullable distincte et une méthode pour l'effacer:

private var _myObject: MyObject? = null

var myObject: MyObject // or val, depending
    get() {
        if (_myObject == null) { _myObject = MyObject() }
        return _myObject!!
    }
    set(value: MyObject) { 
        _myObject?.clear()
        _myObject = value
    }

fun clearMyObject() {
    _myObject?.clear()
    _myObject = null
}

Si vous avez besoin de ce modèle plus d'une fois, écrivez un délégué .


0 commentaires

1
votes

Vous aviez presque trouvé la bonne réponse avec l'opérateur elvis, la seule partie manquante était de réattribuer l'instance nouvellement créée à la variable myObject :

private fun getMyObject() = myObject ?: MyObject().also { myObject = it }


0 commentaires