Kotlin y la forma idiomática de escribir, 'si no nulo, más …' en function del valor mutable

Supongamos que tenemos este código:

class QuickExample { fun function(argument: SomeOtherClass) { if (argument.mutableProperty != null ) { doSomething(argument.mutableProperty) } else { doOtherThing() } } fun doSomething(argument: Object) {} fun doOtherThing() {} } class SomeOtherClass { var mutableProperty: Object? = null } 

A diferencia de Java, donde podría quedarse solo y preocuparse por la desreferenceción nula en el time de ejecución, esto no se comstack, con razón. Por supuesto, mutableProperty ya no puede ser nulo una vez dentro del 'si'.

Mi pregunta es ¿cuál es la mejor manera de manejar esto?

Algunas opciones son evidentes. Sin utilizar ninguna característica nueva del lenguaje Kotlin, la forma más simple es, evidentemente, copyr el valor en un ámbito de método que no cambiará posteriormente.

Hay esto:

 fun function(argument: SomeOtherClass) { argument.mutableProperty?.let { doSomething(it) return } doOtherThing() } 

Esto tiene la desventaja obvia de que necesita regresar temprano o, de lo contrario, evitar ejecutar el código subsiguiente: aceptar en ciertos contexts pequeños, pero huele mal.

Luego está esta posibilidad:

 fun function(argument: SomeOtherClass) { argument.mutableProperty.let { when { it != null -> { doSomething(it) } else -> { doOtherThing() } } } } 

pero si bien tiene una mayor claridad de propósito, podría decirse que es más difícil de manejar y detallado que la forma al estilo Java de tratar con esto.

¿Me estoy perdiendo algo, y hay un idioma preferido para lograr esto?

    No creo que haya una manera realmente "corta" de lograrlo, sin embargo, puedes simplemente usar un condicional dentro o let :

     with(mutableVar) { if (this != null) doSomething(this) else doOtherThing() } mutableVar.let { if (it != null) doSomething(it) else doOtherThing() } 

    Esto es equivalente a su statement de when .

    Siempre existe la opción que describió, asignándola a una variable:

     val immutable = mutableVar if (immutable != null) { doSomething(immutable) } else { doOtherThing() } 

    que siempre es una buena alternativa en caso de que las cosas se vuelvan demasiado detalladas.

    Probablemente no haya una manera muy agradable de lograr esto porque solo se permite colocar el último argumento lambda fuera de () , por lo que especificar dos no se ajustaría realmente a la syntax de todas las otras funciones estándar.

    Podrías escribir uno si no te importa (o si en su lugar pasarás references de methods):

     inline fun <T : Any, R> T?.ifNotNullOrElse(ifNotNullPath: (T) -> R, elsePath: () -> R) = let { if(it == null) elsePath() else ifNotNullPath(it) } 

    Yo usaría let con el operador de Elvis .

     mutableProperty?.let { doSomething(it) } ?: doOtherThing() 

    Del doc:

    Si la expresión a la izquierda de?: No es nula, el operador elvis la devuelve; de ​​lo contrario, devuelve la expresión a la derecha. Tenga en count que la expresión del lado derecho se evalúa solo si el lado izquierdo es nulo.

    Para un bloque de código después de la expresión del lado derecho:

      mutableProperty?.let { doSomething(it) } ?: run { doOtherThing() doOtherThing() } 

    agregue una function en línea personalizada de la siguiente manera:

     inline fun <T> T?.whenNull(block: T?.() -> Unit): T? { if (this == null) block() return this@whenNull } inline fun <T> T?.whenNonNull(block: T.() -> Unit): T? { this?.block() return this@whenNonNull } 

    entonces puedes escribir código como este:

     var nullableVariable :Any? = null nullableVariable.whenNonNull { doSomething(nullableVariable) }.whenNull { doOtherThing() }