So I run into this problem quite often lately, most of the time it happens when I have defined a Hibernate Entity in Kotlin, and I wish to use it in my code.
It happens when there's a property that could conceivably be accessed in another thread, cause the two statements, the if-statement and the execution statement to look at different states, and cause a ClassCastException.
The following code demonstrates the message Kotlin provides when writing such a program:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
private var name: String? = null | |
/** | |
* This does not compile, because after the if and before the assert, name could have been change | |
* in another thread on this object. | |
*/ | |
@Test | |
fun mutablePropertyCouldHaveChanged1() { | |
if (name != null) { | |
// assertThat(name.length).isNotZero() | |
} | |
} |
Possible solutions
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* if we're sure, we can just brute-force is and tell Kotlin that we're sure the property is never null | |
* here. I don't like this. Could be a thread-safety issue. | |
*/ | |
@Test | |
fun mutablePropertyCouldHaveChanged2() { | |
if (name != null) { | |
assertThat(name!!.length).isNotZero() | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* create a local variabel so we're sure it is not a problem. | |
*/ | |
@Test | |
fun mutablePropertyCouldHaveChanged3() { | |
val local = name | |
if (local != null) { | |
assertThat(local.length).isNotZero() | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* use "?.let" to create a lambda in which we're sure there's no problem. | |
* Is a very nice solution, if possible. | |
*/ | |
@Test | |
fun mutablePropertyCouldHaveChanged4() { | |
name?.let { assertThat(it.length).isNotZero() } | |
} |
References
- Youtube - Let, Also, Apply, Run, With - Kotlin Scope Functions
- https://www.youtube.com/watch?v=Vy-dS2SVoHk