Devabc
Devabc

Reputation: 5271

Initializing interface instance variables

I would like to inject (enrich) behavior and state in a Kotlin class by using an interface. Like class Impl : Observable, where Observable contains state.

In Scala using Traits (does work), looking for a Kotlin solution for this one:

object Test extends App {
  val impl = new Impl()
  val observer = new Observer()
  impl.register(observer)
}

trait Observable {
  // How to do this in Kotlin?
  val observers = List()

  def register(observer: Observer) {
    observers.add(observer)
  }
}
class Observer

class Parent
class Impl extends Parent with Observable

Attempt in Kotlin (not working):

fun main(args: Array<String>) {
    val impl = Impl()
    val observer = Observer()
    impl.register(observer)
}

interface Observable {
    // Error: Kotlin: Property initializers are not allowed in interfaces
    val observers = mutableListOf<Observer>()

    fun register(observer: Observer) {
        observers.add(observer)
    }
}
class Observer

open class Parent
class Impl : Parent(), Observable

It results in this error at the interface instance variable line: Kotlin: Property initializers are not allowed in interfaces. How can I initialize instance variables in Kotlin interfaces? (Note that in this design, parent classes shouldn't be altered/updated.)

UPDATE: I had an example solution in Java, but that wasn't behaving correctly. "Instance variables" in Java interfaces are automatically static. So I have removed that example.

Upvotes: 5

Views: 26489

Answers (2)

Jayson Minard
Jayson Minard

Reputation: 85946

You can have a placeholder for state in Kotlin interfaces, and let the implementing class provide that state. So the interface can provide the functionality that works against the expected-to-exist state. Such as:

class Observer

interface Observable {
    val observers: MutableList<Observer> // not present, but expected

    fun register(observer: Observer) {
        observers.add(observer)
    }
}

open class Parent

class Impl : Parent(), Observable {
    override val observers = mutableListOf<Observer>() // used by the interface
}

This is for good reason, there are delicate issues having state coming from more than one item in the class hierarchy and Kotlin is preventing the problems that can arise from such models.

Upvotes: 11

Alexey Romanov
Alexey Romanov

Reputation: 170735

In Java you actually have a static field, not an instance field (unlike Scala code, which does work as you expect):

Every field declaration in the body of an interface is implicitly public, static, and final.

You can achieve the same behavior in Kotlin by putting the field into a companion object.

But what you should actually do is use an abstract class instead of an interface.

Or delegation:

interface Observable {
    fun register(observer: Observer)
}

class ObserverList : Observable {
    val observers = mutableListOf<Observer>()

    override fun register(observer: Observer) {
        observers.add(observer)
    }
}

class Impl : Parent(), Observable by ObserverList()

Upvotes: 3

Related Questions