Search code examples
kotlinnestedinitialization

Using outer class's val/methods as defaults in nested data class declaration


Consider the following toy example:

class A{
    val a=1

    fun hello() =println("hello")

    init {
        println(a)
        println(this::hello)
    }

    data class B(val b: Int = a, val func: () -> Unit = this::hello)
}

This doesn't compile, linting the errors: Unresolved reference: a and 'this' is not defined in this context. This confuses me, since they are available inside the preceding init.

Why are the vals/vars/methods of the parent not accessible in the data class declaration, even though it's nested?

In spite of the plethora of SO questions matching "*kotlin*nested*data*", I couldn't find one answering this.


Solution

  • There are two different kinds of classes that can be declared inside another class:

    • Nested classes: These are just simple class definitions without any connection to the enclosing class except that they must be explicitly qualified with the outer class:

      fun someUnrelatedFunction() {
          val b = A.B()
      }
      

      That's what you have: Two independent classes, two independent instances, two independent this objects. Therefore you cannot access this of the outer class.

    • Inner classes: Inner classes on the other hand can only exist with an instance of the outer class. That means that they cannot be instantiated on themselves. Usually the objects of an inner class are created by the outer class. And in that case the instance of the outer class is available and it can be accessed by ther inner class:

      class A {
          val a = 1
      
          fun hello() = println("hello")
      
          init {
              println(a)
              println(this::hello)
          }
      
          inner class B(val b: Int = a, val func: () -> Unit = this::hello)
      }
      

      This will compile now, because B is defined as an inner class. Note that data classes cannot be inner classes, so I removed data.

      Although this works now, the following doesn't anymore:

      fun someUnrelatedFunction() {
          val b = A.B()
      }
      

      The compiler complains with this error message:

      Constructor of inner class B can be called only with receiver of containing class

      Instead you can create the following property in class A:

      val b = B()
      

    Read more in the documentation about Nested and inner classes.

    As a closing note, since you talked about parent class in your question: Parent classes only exist in inheritance hierarchies where one class extends another. That is entirely unrelated though and has nothing to do with the code from your example. Your code is about an outer and an inner/nested class, not about parent and child classes.