https://kotlinlang.org logo
a

Antoine Gagnon

12/01/2020, 9:35 PM
Hi everyone! Kind of a weird question but I’m trying to restrict access to a method for any subclass. I’m trying to make a builder like this:
Copy code
open class AnimalBuilder {
    val dog: DogBuilder get() = DogBuilder()

    open inner class DogBuilder : AnimalBuilder() {
        val pug: PugBuilder get() = PugBuilder()

        inner class PugBuilder : DogBuilder()
    }
}

val pugBuilder = AnimalBuilder().dog.pug 
val wrongBuilder = AnimalBuilder().dog.dog.dog
But as you can see, I don’t want the
DogBuilder
to be able to access the
dog
variable available in
AnimalBuilder
. Is there any way to do that?
m

mazorius

12/01/2020, 9:44 PM
Can you define the variable like this:
Copy code
private val dog: DogBuilder get() = DogBuilder()
a

Antoine Gagnon

12/01/2020, 9:58 PM
I can’t because I won’t be able to access it with
AnimalBuilder().dog
for instance
j

Joris PZ

12/01/2020, 10:04 PM
I don't think there's a visibility modifier than can hide a property from 'grandchildren' subclasses. Is it really necessary that these builders are related through inheritance?
👆 1
m

Marc Knaup

12/01/2020, 10:17 PM
The builders are a little weird. Ideally you’d use
interface
for each and the classes and class hierarchies are merely an implementation detail. Then you are way more flexible in composing different interfaces and the functionality each has. If you really want to keep it like it is then you can hide
dog
in the subclass by overriding it. 1. Mark the
dog
in
AnimalBuilder
as
open
. 2. Add inaccessible
dog
to `DogBuilder`:
Copy code
@Deprecated(message = "You already have a dog.", level = DeprecationLevel.HIDDEN)
    override val dog: Nothing
        get() = error("Cannot be used.")
It’s also weird that your
DogBuilder
is
inner
in
AnimalBuilder
and also a subclass of
AnimalBuilder
. So it’s linked to two different
AnimalBuilder
instances 🤔
a

Antoine Gagnon

12/01/2020, 10:20 PM
I’ve cleaned it up a bit, and it seems like composition might work better with something like this:
Copy code
open class AnimalBuilderBase(parent: AnimalBuilderBase? = null)

open class AnimalBuilder : AnimalBuilderBase() {
    val dog: DogBuilder get() = DogBuilder(this)

    open class DogBuilder(parent: AnimalBuilder) : AnimalBuilderBase(parent) {
        val pug: PugBuilder get() = PugBuilder(this)

        class PugBuilder(parent: DogBuilder) : AnimalBuilderBase(parent)
    }
}
m

Marc Knaup

12/01/2020, 10:21 PM
Difficult to say without seeing the broader picture 😉
a

Antoine Gagnon

12/01/2020, 10:24 PM
The end goal is to be able to get a string that is a composition of all the different levels, so for something like
AnimalBuilder().dog.pug
I would get
Dog - Pug
but for
AnimalBuilder().dog.greyhound
I would get
Dog - Greyhound
with the DogBuilder level adding only the “Dog” string and the PugBuilder level adding the “Pug” string
m

Marc Knaup

12/01/2020, 10:25 PM
How do you get the final result out of it?
a

Antoine Gagnon

12/01/2020, 10:26 PM
Something like a
build
function that would be part of
AnimalBuilderBase
and would return the concatenation of all of those
m

Marc Knaup

12/01/2020, 10:30 PM
Here’s another approach: https://pl.kotl.in/vwqBnaNLi
I’ve used
toString()
but you can also use
build()
.
a

Antoine Gagnon

12/01/2020, 10:35 PM
I like that a lot too! Thanks for the help. much appreciated!
👍 1