web-dev-qa-db-ja.com

「When」ステートメントvs Java "switch"ステートメント

Kotlinのパターンマッチングは優れており、次のパターンマッチングを実行しないという事実は、ユースケースの90%で良好です。

Androidでは、データベースが更新されたときに、Java switchプロパティを使用して、次のケースに進むために、次のようなコードにブレークを入れない場合:

switch (oldVersion) {
    case 1: upgradeFromV1();
    case 2: upgradeFromV2(); 
    case 3: upgradeFromV3();
}

したがって、誰かがDBのバージョン1のアプリを使用していて、DB v2のアプリのバージョンを見逃した場合、必要なすべてのアップグレードコードが実行されます。

Kotlinに変換すると、次のような混乱が生じます。

when (oldVersion) {
    1 -> {
        upgradeFromV1()
        upgradeFromV2()
        upgradeFromV3()
    }
    2 -> {
        upgradeFromV2()
        upgradeFromV3()
    }
    3 -> {
        upgradeFromV3()
    }
}

ここには3つのバージョンしかありません。DBがバージョン19に達すると想像してください:/

とにかくスイッチよりも同じように行動するときに作る?私は運なしで続けようとしました。

52
Geob-o-matic

シンプルだが冗長な解決策は次のとおりです。

if (oldVersion <= 1) upgradeFromV1()
if (oldVersion <= 2) upgradeFromV2()
if (oldVersion <= 3) upgradeFromV3()

関数参照 を使用した別の可能な解決策:

fun upgradeFromV0() {}
fun upgradeFromV1() {}
fun upgradeFromV2() {}
fun upgradeFromV3() {}

val upgrades = arrayOf(::upgradeFromV0, ::upgradeFromV1, ::upgradeFromV2, ::upgradeFromV3)

fun upgradeFrom(oldVersion: Int) {
    for (i in oldVersion..upgrades.lastIndex) {
        upgrades[i]()
    }
}
63
bashor

編集:以下の元の応答。私が現在していることは次のとおりです。

fun upgrade() {
    fun upgradeFromV1() { /* Do stuff */ }
    fun upgradeFromV3() { /* Do stuff */ }

    tailrec fun upgradeFrom(version: Int): Unit = when (version) {
        LATEST_VERSION -> {
            Config.version = version
        } 1 -> {
            upgradeFromV1()
            upgradeFrom(2)
        } in 2..3 -> {
            upgradeFromV3()
            upgradeFrom(4)
        } else -> {
            Log("Uncaught upgrade from $version")
            upgradeFrom(version+1)
    }

    upgradeFrom(Config.version)
}

これが@ C.A.Bの答えのバリエーションです。与えた:

fun upgrade(oldVersion: Int) {
    when (oldVersion) {
        latestVersion -> return
        1 -> upgradeFromV1()
        2 -> upgradeFromV2()
        3 -> upgradeFromV3()
    }
    upgrade(oldVersion + 1)
}
17
Julian Delphiki

これはどう:

_fun upgradeFromV3() {/* some code */}
fun upgradeFromV2() {/* some code */ upgradeFromV3()}
fun upgradeFromV1() {/* some code */ upgradeFromV2()}
fun upgradeFromV0() {/* some code */ upgradeFromV1()}

fun upgrade(oldVersion: Int) {
    when (oldVersion) {
        1 -> upgradeFromV1()
        2 -> upgradeFromV2()
        3 -> upgradeFromV3()
    }
}
_

追加:

アップグレードパスをリストとして定義する @ lukle のアイデアが気に入っています。これにより、初期段階ごとに異なるアップグレードパスを定義できます。例えば:

  1. リリース済みバージョンから最新リリース済みバージョンへのシンプルな高速パス
  2. ホットフィックスバージョンからのキャッチアップパス(数行連続する可能性があります)。以前のフルバージョンから次のフルバージョンに移行する場合は適用しないでください。

そのためには、リストのどの要素から適用するかを知る必要があります。

_fun <Vs, V> Pair<Vs, V>.apply(upgrade: () -> Unit): (V) -> V {
    return { current: V ->
        if (first == current) {
            upgrade()
            second
        } else {
            current
        }
    }
}

val upgradePath = listOf(
        (0 to 10).apply  { /* do something */ },
        (5 to 15).apply  { /* do something */ },
        (10 to 20).apply { /* do something */ },
        (15 to 20).apply { /* do something */ },
        (20 to 30).apply { /* do something */ },
        (30 to 40).apply { /* do something */ }
)

fun upgrade(oldVersion: Int) {
    var current = oldVersion
    upgradePath.forEach { current = it(current) }
}
_

このコードでは、VsはVと同じ、またはequals(other: Any?): BooleanメソッドをオーバーライドしたV値のコレクションのいずれかです。

11
C.A.B.

OPの答えの別のバリエーション:

override fun onUpgrade(db: SQLiteDatabase, oldVersion: Int, newVersion: Int) {
    when (oldVersion) {
        newVersion -> return
        1 -> TODO("upgrade from v1 to v2")
        2 -> TODO("upgrade from v2 to v3")
    }
    oldVersion++
    onUpgrade(db, oldVersion, newVersion)
}
2
arslancharyev31

公式リファレンスから絶対に可能な引用です: 制御フロー:if、when、for、while

If many cases should be handled in the same way, the branch conditions may be combined with a comma:

when (x) {
    0, 1 -> print("x == 0 or x == 1")
    else -> print("otherwise")
}

したがって、同じ条件リストが短い場合は、それらをコマで区切ってリストするか、他の回答に記載されているように1..10の条件のような範囲を使用できます

2
Yarh

Forループはwhenで使用できます。

for (version in oldVersion..newVersion) when (version) {
    1 -> upgradeFromV1()
    2 -> upgradeFromV2()
    3 -> upgradeFromV3()
}
2
John

以下は、bashorからの2つの回答と、機能的な砂糖を少し混ぜたものです。

fun upgradeFromV0() {}
fun upgradeFromV1() {}
fun upgradeFromV2() {}
fun upgradeFromV3() {}

val upgrades = arrayOf(::upgradeFromV0, ::upgradeFromV1, ::upgradeFromV2, ::upgradeFromV3)

fun upgradeFrom(oldVersion: Int) {
    upgrades.filterIndexed { index, kFunction0 -> oldVersion <= index }
            .forEach { it() }
}
0
lukle

カスタム実装用のKotlin DSLはどうですか?このアプローチのようなもの:

class SwitchTest {

    @Test
    fun switchTest() {

        switch {
            case(true) {
                println("case 1")
            }
            case(true) {
                println("case 2")
            }
            case(false) {
                println("case 3")
            }
            caseBreak(true) {
                println("case 4")
            }
            case(true) {
                println("case 5")
            }
//          default { //TODO implement
//
//          }
        }
    }
}

class Switch {
    private var wasBroken: Boolean = false

    fun case(condition: Boolean = false, block: () -> Unit) {
        if (wasBroken) return
        if (condition)
            block()
    }

    fun caseBreak(condition: Boolean = false, block: () -> Unit) {
        if (condition) {
            block()
            wasBroken = true
        }
    }
}

fun switch(block: Switch.() -> Unit): Switch {
    val switch = Switch()
    switch.block()
    return switch
}

以下を出力します:case 1 case 2 case 4 UPD:いくつかのリファクタリングと出力例。

0