Question about shared state in class hierarchy


#1

I am working on my builder hierarchy and I'm stuck on a design / implementation point.

I would like to have a shared, private object called “builder”, which is an HDSBuilder (from a Java framework) and use that single instance in all sub class instances.

For example:

abstract class N

abstract class TestNode(): N() {

  var builder : HDSBuilder

  open protected fun initNode<T: N>(tag: T, init: T.() -> Unit): T {
  builder.pushNode(“test”)
  tag.init()
  builder.popNode()
  return tag
  }

  fun assert(init: AssertNode.() ->Unit) = initNode(AssertNode(), init)
}


class AssertNode(): TestNode() {
  override fun initNode<T: N>(tag: T, init: T.() -> Unit): T {   builder.pushNode("assert")   tag.init()   builder.popNode()   return tag   } }

class TestLIstRoot() : TestNode() {

  override var builder : HDSBuilder = HDSBuilder()

  fun test(init: TestNode.() -> Unit) = initNode(TestListRoot(), init)

  override fun initNode<T: N>(tag: T, init: T.() -> Unit): T {
  builder.pushNode(“test”)
  tag.init()
  builder.popNode()
  return tag
  }
}

What is the best way to do this in Kotlin?

– Randy


#2

A protected property should work fine, but it looks like you know that. What exactly is the problem?


#3

Andrey,

In this particular example, AssertNode won’t compile. IntelliJ suggests that I need to implement a member and gives me this code suggestion:

  override var builder: HDSBuilder = ?

But wants me to provide an initial value.

I don’t want each sub class to reinitialize the builder, I want to use the single builder from the super class.

I am not understanding how I would have a reference in a sub class to a member protected instance variable in the super class.

Maybe I’m not thinking about this the right way and you could suggest a different pattern.

Thank you!

Randy


#4

Why don't you initialize the builder property in the super class, and then just reference it by name in subclasses?


#5

Andrey,

If I initialize the builder property in the super class (TestNode), then when I instantiate any sub-class (e.g. AssertNode), it will cause its parent class to initialize and that will initialize a new builder instance.

I need a class static value that is created once when the first TestNode class is brought to life by the classloader. In Java:

static {
  HDSBuilder builder = new HDSBuilder();
  }

I have read about the class object concept as that seems to be Kotlin’s answer to class static values, but I’m a bit slow today, I can’t figure out how to make that work either.

– Randy


#6

Andrey,

I figured it out. I’m going to refine this, but here is the code I have working now. (Thanks to the Java -> Kotlin code converter I discovered the correct way to use the class objects!

  public fun onSource(context: INKFRequestContext): Unit {

  val testlist: IHDSNode = testListBuilder(“testlist”) {

&nbsp;&nbsp;test {
  &nbsp;&nbsp;request { }
  &nbsp;&nbsp;assert { }
&nbsp;&nbsp;}

&nbsp;&nbsp;test {
&nbsp;&nbsp;}

  }

  System.out.println(testlist)

  val response: INKFResponse = context.createResponseFrom("<hello>foo</hello>")
  response.setMimeType(“text/xml”)
  response.setExpiry(INKFResponse.EXPIRY_ALWAYS)
  }
}

object builder : HDSBuilder()

fun newBuilder(): HDSBuilder {
  return builder
}

abstract class N

abstract class Node(): N() {
  class object {
  protected val builder: HDSBuilder = HDSBuilder()
  }

  open public fun initNode<T: N>(name: String, tag: T, init: T.() -> Unit): T {
  builder.pushNode(name)
  tag.init()
  builder.popNode()
  return tag
  }

}

class TestNode(): Node() {
  fun request(init: RequestNode.() ->Unit) = initNode(“request”, RequestNode(), init)
  fun assert(init: AssertNode.() ->Unit) = initNode(“assert”, AssertNode(), init)
}

class AssertNode(): Node() {
}

class RequestNode(): Node() {
}

class TestListRoot(): Node() {
  public fun getRootNode(): IHDSNode {
  return builder.getRoot()
  }

  fun test(init: TestNode.() -> Unit) = initNode(“test”, TestNode(), init)
}

fun testListBuilder(name:String, init: TestListRoot.() -> Unit): IHDSNode {
  val testListRoot = TestListRoot()
  testListRoot.initNode(name,TestListRoot(), init)
  return testListRoot.getRootNode()
}


#7

Note: alternatively to using class objects, you can have a top-level property (make it private to the package, for example).