Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

sealed class's objects mysteriously becoming null when referenced by other companion object

Tags:

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>
like image 919
smallufo Avatar asked Dec 05 '17 07:12

smallufo


1 Answers

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.

like image 67
smallufo Avatar answered Sep 22 '22 17:09

smallufo