How can I make nested class constructors accessible only by surrounding class?

#1

I want to prevent that someone creates an instance directly without invoking a certain factory method. In the following example only Container should be able to create Item instances.

This Scala examples shows what I want:

class Container {
  def createItem(name: String) = new Item(name, this)
  class Item private[Container](name: String, container: Container)
}

But Kotlin has no concept of scoped privacy. The following doesn’t compile:

class Container {
    fun createItem(name: String) = Item(name, this) // constructor inaccessible
    class Item private constructor(val name: String, container: Container)
}

What comes close is a separate interface:

class Container {

    fun createItem(name: String): Item = ContainerItem(name, this)

    private class ContainerItem(
        override val name: String,
        override val container: Container
    ) : Item
}

interface Item {
    val name: String
    val container: Container
}

But compared to Scala this is lengthy and has no benefit (at least not in my case).

Is there an easier way in Kotlin?

And wouldn’t it be nice if Kotlin had something like private[this], private[Something]?

#2

Now that I see this post I remember I had the same problem a while ago. I didn’t find a good solution. I ended up with an internal constructor, maybe this is a workable solution for you as well.

I’m not sure I like the idea of private[this], private[Something] though. It feels a bit too complex for something minor.
I personally would be fine with allowing maybe the inner keyword to be used as a visibility modifier as long as the class is nested. (inner private fun foo() = TODO())

On the JVM this could probably be achieved the same way as internal. A public function which is marked as synthetic, so that java won’t call it, plus a rule in kotlin that you can only call it from within the nested or outer class.

#3

You could make it an inner class. I’m pretty sure only the parent is able to instantiate its inner classes.

An added bonus is that you don’t need a container property, since inner classes have an implicit reference to their parent class, with this@Container.

class Container {
    fun createItem(name: String) = Item(name)

    inner class Item(val name: String)
}
#4

Inner classes should be instantiatable from the outside like this:

val outer = Outer()
val inner = outer.Inner()
1 Like
#6

Inner classes should be instantiatable from the outside like this:

val outer = Outer()
val inner = outer.Inner()

That is exactly what I want to disallow.

#7

:smiley: finally i see:


you don’t need private constructor and fabric for inner class, because your public constructor of inner class is a factory itself
scoped privacy gives you a nicer strict fabric for java-styled code with scala, but you got correctly created inner instance without it (as mentioned you also don’t need to store container for inner class, it already stored)
so “inner class” is more “kotlin” way to make nested instance, it’s always produced by defined parent instance, it’s always have access to it, and you cant “avoid to invoke” fabric when consructor is a fabric

#8

@alexey_e , you’r right in my example an inner class would be sufficient. I completed your example with the relevant reference to the outer class:

 fun main(args: Array<String>) {
	val c = Container("container")
    val i = c.Item("item")
    println(i)
}

class Container(val name: String) {
    inner class Item(val name: String) {
        val container = this@Container
        override fun toString() = "name: $name, container: ${container.name}"
    }
}

executable example

But just today I had another need for Scalasprivate[this]`. It would still be nice to have such a thing in Kotlin (though I have to admit that you see it rarely in Scala).