web-dev-qa-db-ja.com

可変値をベースにした「nullでない場合は...」と書くKotlinと慣用的な方法

次のコードがあるとします:

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
}

Javaの場合とは異なり、実行時にnullの逆参照を心配するために放置される可能性がありますが、これはコンパイルされません。もちろん、mutablePropertyは 'if'内で一度nullになることはありません。

私の質問は、これを処理する最良の方法は何ですか?

いくつかのオプションが明らかです。新しいKotlin言語機能を使用せずに、最も簡単な方法は、明らかに値をメソッドスコープにコピーすることです。

これがあります:

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

これには、早めに戻る必要がある、または後続のコードの実行を避ける必要があるという明らかな欠点があります。特定の小さなコンテキストでは問題ありませんが、臭いがあります。

次に、この可能性があります:

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

しかし、目的がより明確になっている一方で、おそらく、これを扱うJavaスタイルの方法よりも扱いにくく冗長です。

私は何かが欠けていますか?これを達成するための好ましいイディオムはありますか?

22
Rob Pridham

私はそれを達成するための本当に「短い」方法があるとは思わないが、あなたは単にwithまたはlet内で条件を使うことができる:

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

実際、可変値の「キャプチャ」は、letの主な使用例の1つです。

これはwhenステートメントと同等です。

説明したオプションが常にあり、それを変数に割り当てます。

val immutable = mutableVar

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

これは、たとえば、物事は冗長になりすぎます。

Niceこれを達成する方法は、おそらくlastラムダ引数は()の外側に置くことができます。そのため、2を指定しても、他のすべての標準関数の構文には実際には適合しません。

気にしない場合(またはメソッド参照を渡す場合)は、couldで記述できます。

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

...

val a: Int? = null
a.ifNotNullOrElse({ println("not null") }, { println("null") })
12
Moira

更新:

コメントでフランタが述べたように、メソッドdoSomething()がnullを返す場合、elvis演算子の右側のコードが実行されますが、ほとんどの場合、これは望ましいケースではありません。しかし同時に、この場合、doSomething()メソッドは何かを行うだけで、何も返さない可能性が非常に高いです。

代替案:プロトタイプがコメントで言及したように、alsoは関数ブロックの結果ではなくletオブジェクトを返すため、alsoではなくthisを使用できます。

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

元の答え:

letエルビス演算子 とともに使用します。

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

ドキュメントから:

?:の左側の式がnullでない場合、elvis演算子はそれを返し、そうでない場合は右側の式を返します。右側の式は、左側がヌルの場合にのみ評価されることに注意してください。

右側の式の後のコードブロックの場合:

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

以下のようにカスタムインライン関数を追加します。

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
}

次のようなコードを書くことができます:

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

次のようなこともできます。

class If<T>(val any: T?, private val i: (T) -> Unit) {
    infix fun Else(e: () -> Unit) {
        if (any == null) e()
        else i(any)
    }
}

その後、次のように使用できます。

If(nullableString) {
   //Use string
} Else {

}
0
sunilson

私は通常このように書きます:

  takeIf{somecondition}?.also{put somecondition is met code}?:run{put your else code here}

takeIfが必須である後の疑問符に注意してください。キーワードを使用することも、キーワードを適用することもできます。

0
j2emanue