Pattern matching in Kotlin is nice and the fact it does not execute the 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 versions, imagine when DB reaches version 19.
Anyway to makes when acting in the same way then switch? I tried to continue without luck.
Kotlin has a simple and concise syntax. Codes written in this language have better readability than equivalent codes written in Java. Below are three different ways to write a simple function that adds two numbers together. The first 2 examples are written in Kotlin, while the third is written in Java.
In Kotlin, if a matching case is found, only the code in the respective case block is executed, and execution continues with the next statement after the when block. This essentially means that we don't need break statements at the end of each case block.
In Java we use switch but in Kotlin, that switch gets converted to when. when is also used for conditional representation but it does the things in a very smarter and easier way. Whenever you are having a number of possibilities then you can use when in your code.
it can be used without an argument.
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]()
}
}
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)
}
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:
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.
You can just use for loop with when.
for (version in oldVersion..newVersion) when (version) {
1 -> upgradeFromV1()
2 -> upgradeFromV2()
3 -> upgradeFromV3()
}
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With