可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
Pattern matching in Kotlin is nice and the fact it does not execute next pattern match is good in 90% of use cases.
In Android, when database is updated, we use Java switch property to go on next case if we do not put a break to have code looking like that:
switch (oldVersion) {
case 1: upgradeFromV1();
case 2: upgradeFromV2();
case 3: upgradeFromV3();
}
So if someone has an app with version 1 of the DB and missed the app version with DB v2, he will get all the needed upgrade code executed.
Converted to Kotlin, we get a mess like:
when (oldVersion) {
1 -> {
upgradeFromV1()
upgradeFromV2()
upgradeFromV3()
}
2 -> {
upgradeFromV2()
upgradeFromV3()
}
3 -> {
upgradeFromV3()
}
}
Here we have only 3 version, imagine when DB reaches version 19 :/
Anyway to makes when acting in the same way than switch? I tried continue without luck.
回答1:
Simple but wordy solution is:
if (oldVersion <= 1) upgradeFromV1()
if (oldVersion <= 2) upgradeFromV2()
if (oldVersion <= 3) upgradeFromV3()
Another possible solution with function references:
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]()
}
}
回答2:
edit: Original response below. Here's what I'm currently doing:
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)
}
Here's a variation on the answer @C.A.B. gave:
fun upgrade(oldVersion: Int) {
when (oldVersion) {
latestVersion -> return
1 -> upgradeFromV1()
2 -> upgradeFromV2()
3 -> upgradeFromV3()
}
upgrade(oldVersion + 1)
}
回答3:
How about this:
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()
}
}
Added:
I like the idea by @lukle to define the upgrade path as a list. This allows to define different upgrade paths for different initial stage. For example:
- Simple fast path from released version to the latest released version
- Catch-up path from hot-fix version (could be few in a row), which should not be applied when going from previous full version to the next full version
For that we need to know from which elements of the list to apply.
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) }
}
In this code Vs could be the same as V or some kind of collection of V values with overridden equals(other: Any?): Boolean
method.
回答4:
It is absolutly possible
quote from official reference : Control Flow: 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")
}
So if same condition list is short, then you can list them separating by coma, or use ranges like condition in 1..10 as stated in other answers
回答5:
Here is a mix of the two answers from bashor, with a little bit of functional sugar:
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() }
}
回答6:
Another variation of OP's answer:
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)
}
回答7:
What about Kotlin DSL for custom implementation? Something like this approach:
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
}
It prints:
case 1
case 2
case 4
UPD: Some refactorings and output example.
回答8:
val orders = arrayListOf(
{ upgradeFromV1()},
{ upgradeFromV2()},
{ upgradeFromV3()}
)
orders.drop(oldVersion).forEach { it() }
回答9:
Kotlin works with a different flow control called when.
Your code, using it when, can be that way.
Obviously the code could be different, but I understand that your question is only about the use of switch.
fun main(args: Array<String>) {
val month = 8
val monthString = when(month) {
1 -> "Janeiro"
2 -> "February"
3 -> "March"
4 -> "April"
5 -> "May"
6 -> "June"
7 -> "July"
8 -> "August"
9 -> "September"
12 -> "October"
11 -> "November"
10 -> "December"
else -> "Invalid month"
}
println(monthString);
}