What was the reason to diverge from Java in this point? I can’t seem to find the rationale in the docs or on this site.
Probably because the JVM actually can’t and Java generates appropriate synthetic accessors. Kotlin just tries to limit the idea in the first place.
Wow, I didn’t know that. Still, why can’t Kotlin do the same? It isn’t that language designers shy away from generating bytecode
Suppose there’s a tree type and a tree node type. You want to allow modifying node pointers only from the tree code, because it preserves some invariants. In Java, you can simply make the Node a (static) inner class of Tree, make the pointer fields private, have a public getter and then either use a private setter or simply manipulate the pointer from the Tree class. Seems like there’s no good way to do that in Kotlin.
class Tree {
private val root = Node()
fun move(node: Node, after: Node) {
// Need to manipulate pointers while maintaining invariants.
// E.g. need to say after.nextSibling = node, but how?
}
class Node(
parent: Node? = null,
firstChild: Node? = null,
nextSibling: Node? = null
) {
var parent: Node? = parent; private set
var firstChild: Node? = firstChild; private set
var nextSibling: Node? = nextSibling; private set
}
}
In most cases you can get away with making the type itself private. In your case you’ll have to go the extra way by having a Node interface and a NodeImpl class (which is private). Btw. trees are typically implemented immutable (but indeed don’t have to).
Just declare your node impl as a private top level class in the same file.
The point is that in a tree you would want your node to be public but have members which can only be accessed by the tree itself, so your node can not be just private
The solution (admittedly not quite perfect) is to have the following:
class Tree {
val root: Node = NodeImpl
fun move(node: Node, after: Node) { /* some impl */ }
interface Node {
val parent: Node?
val firstChild: Node?
val nextSibling: Node?
}
private class NodeImpl(override var parent: NodeImpl? = null,
override var firstChild: NodeImpl? = null,
override var nextSibling: NodeImpl? = null): Node
}
This should work although it doesn’t stop “invalid” nodes from being passed in by users, but node ownership is always a problem in mutable trees (copying to a locally created not is always safer in that sense).
If you are separating your code into different modules you can even use an abstract class
with an internal constructor
instead of the interface. That way no one outside of the module can create “invalid” nodes.
Oh, I see. I somehow missed that you can override val
with var
. Thanks!
As for stopping from passing “invalid” nodes — Node
can be a sealed
interface
You’re right. I did forget about sealed
as I have not used it myself so far
Kotlin allows to do that but uses different default behavior.
Inner class in Kotlin by default behave like Java’s class with static
modifier on inner class. So it’s completely separate class without reference to the parent.
If you want to get access to parent class like in java without static
you should add modifier inner
to Kotlin’s internal class.
It just forces you to be more explicit for such case and it’s harder to leak parent class accidentally like in java, when you just forgot to mark inner class static, it’s actually not the best practice in most cases
Gildor : He (and I) want to do the exact opposite of “inner”. Tree wants to modify the internals of Node, but also wants to hang out Node(s) to other clients that look immutable. Only Tree should be able to modify the guts of Node.
The solution given works, but… it’d be nice if the language supported it, instead of having to write boiler plate for this pattern every time.