Parent of a {data} class

It’s possible to get a parent object in view hierarchy in Android, why shouldn’t we can get same in flat Kotlin? E.g.

data class Foo(val num: Int = 42){
    fun pass(){
        if (this.parent is Bar) val chad = Chad(this.parent)
    }
}

class Bar(){
    val foo = Foo()
}

class Chad(val bar: Bar)

The view hierarchy is a tree structure, so naturally every view has a unique parent.

The attributes graph is not a tree structure. Objects don’t have a unique parent - some have multiple objects in which they are references, some have none.

Consider the following examples:

class Foo() {}
class Bar(val foo: Foo) {}
class Tuk(val foo: Foo) {}

fun main() {
    val foo = Foo()
    val bar1 = Bar(foo)
    val bar2 = Bar(foo)
    // What is foo.parent? bar1 or bar2 or something else?
    val foo2 = Foo()
    val bar = Bar(foo)
    val tuk = Tuk(foo)
    // What is foo2.parent? bar or tuk or something else?
}

2 Likes

Array of parents then.

Well, it is technically possible, but would be a huge overhead and since it is not needed for most programs, it would not be advisable to make this obligatory for any class.

What is the use case you want to solve with that?

1 Like

Not sure. I found a situation like in my code sample where I need to send a class instance inside its subclass. And it looks not fine to use clean functions there. So, I send this parent class inside the subclass but maybe it’s not necessary.

I guess it’s best to implement the parent attribute by yourself in the classes where it is actually needed, e.g.

data class Foo() {
    // Edit: removed `private`, see reply by Psijic.
    var parent: Parent? = null
}

class Bar() {
    var foo = Foo()
        set(value) {
             field.parent = null
             field = value
             field.parent = this
             // inform previous parent about new affiliation?
        }
}

If (like in your example) there are multiple classes whose instances can be parents, I would move the setting-part from Bar into an interface Parent; if multiple parents are possible, a list attribute can be used instead of an optional. The exact implementation details always should be adapted to your actual use-case.

2 Likes

You can’t set a private var here. And what’s the case with field.parent = null first? But the method looks usable.

Sorry, my mistake: Force of habit.

The attribute is being set to a new value in that moment. That means that the old value of the attribute will not be a child any longer. Therefore, this “old” child gets informed that it now does not have a parent anymore: field.parent = null:

    bar.foo = foo1
    bar.foo = foo2
    // now bar is not parent of foo1 anymore (foo1.parent == null)

Suppose, it should send a message then or be reactive, not just make it null first and something after. But maybe it’s ok. The solution is fine if it’s possible to encapsulate var parent, otherwise looks not good if the field is public for set.

That’s not exactly accurate.

Look again at the example above:

    bar.foo = foo1
    bar.foo = foo2

Now, let’s look at the call of the set method from the second statement:

    field.parent = null // means  foo1.parent = null
    field = value       // means  bar.foo = foo2
    field.parent = this // means  foo2.parent = bar

As you can see, three attributes are set and none of them gets “made null first and something after”.

Yes, that’s why I added it. But what about encapsulation?

The bar objects need to have set access - so to make set access private, Bar must be in reach of private scope.

One way to do that would be to make Bar a nested class in Foo:

class Foo() {
    var parent: Bar? = null
        private set

    class Bar() {
        var foo = Foo()
            set(value) {
                field.parent = null
                field = value
                field.parent = this
                // inform previous parent about new affiliation?
            }
    }
}

fun usageExample() {
    val bar = Foo.Bar()
    bar.foo = Foo()
    println(bar.foo.parent)
}

Another way would be to actually store the structure within the Bar class and make the parent property purely virtual (“computed”) while just getting the value from the internal parents store of Bar:

class Foo() {
    val parent: Bar?
        get() = Bar.getParentOf(this)
}

class Bar() {
    var foo = Foo()
        set(value) {
            parents.remove(field)
            field = value
            parents.put(field, this)
            // inform previous parent about new affiliation?
        }

    companion object {
        private val parents = mutableMapOf<Foo, Bar>()

        fun getParentOf(foo: Foo): Bar? = parents.get(foo)
    }
}

fun usageExample() {
    val bar = Bar()
    bar.foo = Foo()
    println(bar.foo.parent)
}

It should be noted that for this solution, it is imperative that the hash/equal-conditions for maps are satisfied by Foo class.

Again: If more classes are possible as parents, the logic should be moved into Parent interface.

1 Like

Oh, it’s long. Simpler to add the parent inside as a construction parameter. Especially for a single one. But nested class could be usable well especially if the variable is not in an interface.

Are you trying to use classes and data classes as some specific data structure?

Instead of using the language types to create a model, sometimes people try to use the language “class”, without change, to model domain data structure by itself.

For example, trying to use a data class to represent any Json Object. And implementing it with reflection to access properties to represent whatever fields are present, or trying to dynamically construct or add member properties to a class to match the json schema.
Instead, it’s better to create a new model and make a class `JsonObject’ and store fields as normal members of that type.

Your exact data structure probably isn’t Json but the point still applies. If you find yourself wanting “data class” (or normal classes) to have business rules that’s a good hint you are misusing the features of classes to fill in for those rules.


EDIT:
Just wanted to add:
Some languages tell you to do the opposite and use the language’s ontology without much change.

For example, Mint has you model your code not into “classes” but into “components”. Mint positions itself so that when you are modeling your problem, you’re starting in Mint’s domain of a web application.

If you were writing your app in most general-purpose languages like Kotlin, you would use the language’s built-in ontology of building blocks like “classes” to create a new custom ontology for web development.
Since we create the new ontology, we no longer expect the language’s built-in classes to behave in a specific way. We can model systems with multiple parents (multiple-inheritance), data structures that return all of their components (componentN), and more.

If we wanted web apps in the style of Mint-lang, we could model core web types without the need for Kotlin to add built-in support for web styling to the concept of a “class”.

1 Like