There is a sealed class :
sealed class Alphabet(val name: String) {
object A : Alphabet("A")
object B : Alphabet("B")
object C : Alphabet("C")
object D : Alphabet("D")
object E : Alphabet("E")
companion object {
val array = arrayOf(A, B, C, D, E)
val list = listOf(A, B, C, D, E)
}
override fun toString(): String {
return name
}
}
And there is another class with companion object :
class AlphabetMap {
companion object {
val map = mapOf(
Alphabet.A to 1,
Alphabet.B to 2,
Alphabet.C to 3,
Alphabet.D to 4,
Alphabet.E to 5
)
}
}
And if I want to print array (or list) :
class AlphabetTest {
@Test
fun printValues() {
Alphabet.array.forEach { print("$it ") };println()
Alphabet.list.forEach { print("$it ") };println()
}
}
It correctly print the results :
A B C D E
A B C D E
but if I initialize the AlphabetMap
in the code :
class AlphabetTest {
val m = AlphabetMap()
@Test
fun printValues() {
Alphabet.array.forEach { print("$it ") };println()
Alphabet.list.forEach { print("$it ") };println()
}
}
The result mysteriously becomes :
null B C D E
null B C D E
The first element (A) becomes null
If I define
val m = AlphabetMap
The result is the same.
If I init AlphabetMap in the function :
@Test
fun printValues() {
val m = AlphabetMap() // or val m = AlphabetMap
Alphabet.array.forEach { print("$it ") };println()
Alphabet.list.forEach { print("$it ") };println()
}
The result is the same :
null B C D E
null B C D E
but if I init like this :
@Test
fun printValues() {
Alphabet.array.forEach { print("$it ") };println()
val m = AlphabetMap() // or val m = AlphabetMap
Alphabet.list.forEach { print("$it ") };println()
}
Everything works fine now :
A B C D E
A B C D E
If I rewrite the AlphabetMap to
class AlphabetMap {
companion object {
val map = mapOf(
//Alphabet.A to 1,
Alphabet.B to 2,
Alphabet.C to 3,
Alphabet.D to 4,
Alphabet.E to 5
)
}
}
The result becomes
A null C D E
A null C D E
And if AlphebetMap is :
class AlphabetMap {
companion object {
val map = mapOf(
Alphabet.E to 5
)
}
}
The result becomes :
A B C D null
A B C D null
What could go wrong ? Is it a bug ? or language feature ?
Environments : jdk1.8.0_144 , OS X
<kotlin.version>1.2.0</kotlin.version>
<kotlin.compiler.jvmTarget>1.8</kotlin.compiler.jvmTarget>
<kotlin.compiler.incremental>false</kotlin.compiler.incremental>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-stdlib</artifactId>
<version>${kotlin.version}</version>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-stdlib-jdk8</artifactId>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-reflect</artifactId>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlinx</groupId>
<artifactId>kotlinx-coroutines-core</artifactId>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-test-junit</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-test</artifactId>
<scope>test</scope>
</dependency>
The only way to get around this problem is to move the map definition outside the class :
val map = mapOf(
Alphabet.A to 1,
Alphabet.B to 2,
Alphabet.C to 3,
Alphabet.D to 4,
Alphabet.E to 5
)
class AlphabetMap {
companion object {
}
}
According to JetBrain's reply on youtrack and reddit , this is As Designed , no way to solve it.
The problem of cycles in initialization has no general solution. No matter which exact rules we come up with, if the initializer of the object A accesses object B and the initializer of object B accesses object A, one of them will be able to observe the other in an uninitialized state.
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