I'm confused by the protected modifier

Could you explain me why the line it.protectedProperty = 0 in the CompositeImpl class doesn’t work?

// Kotlin 1.4.21

sealed class Component {
    var protectedProperty: Int = 0
        protected set

    abstract class Leaf : Component()

    abstract class Composite : Component() {
        abstract val children: List<Component>
    }
}

class CompositeImpl : Component.Composite() {
    private val _children = arrayListOf<Component>()

    override val children: List<Component> by ::_children

    fun a() {
        // This works.
        protectedProperty = 0
    }

    fun b() {
        children.forEach {
            // This does not work.
            // error message: Cannot assign to 'protectedProperty': the setter is protected in 'Component'
            it.protectedProperty = 0
        }
    }
}

protected — same as private + visible in subclasses too;
(https://kotlinlang.org/docs/reference/visibility-modifiers.html)

I thought the code would work because CompositeImpl is a subclass of Component
Am I misunderstanding the protected modifier?

1 Like

it’s type is Component (which doesn’t have that property), not CompositeImpl. If it’s type were CompositeImpl (by casting, or some other way) then that work

1 Like

Thanks for your reply!

I added new code to fun b()

// class CompositeImpl
fun b() {
    children.forEach {
        // This does not work.
        // error message: Cannot assign to 'protectedProperty': the setter is protected in 'Component'
        it.protectedProperty = 0
    }

    (it as? Component)?.protectedProperty = 0 // error
    (it as? Composite)?.protectedProperty = 0 // error
    (it as? CompositeImpl)?.protectedProperty = 0 // OK
}

CompositeImpl is a subclass of Composite and Component.
Why does only the last one (casting to CompositeImpl) work?

it’s type is Component (which doesn’t have that property)

Could you explain more about the “which doesn’t have that property” part?
I have declared that property in Component.

2 Likes

It does. It’s exactly Component where that property is declared. So there must be another reason.

My guess is that it is done to only allow classes to access their own properties. When you access this.protectedProperty, it’s OK because once you inherited that property, its your own business what you do to it. But when you access it.protectedProperty, and all you know about it is that it some subclass of Component, then it’s no longer your business. If such access was allowed, then in order to understand what happens to a certain property, one would need to locate all subclasses and study their source code.

Example. Suppose Component is a part of a library A, and CompositeImpl in in some 3rd party plugin B that uses the library. Now, a user of the library developing module C declares a subclass of Component and calls it MyComponent. It inherits that property, and uses it for its own purposes. Then, at some point, that CompositeImpl randomly changes this property of one of its children which happens to be of MyComponent class at run time. Now the developer of C is really confused because all they know is that they didn’t change it, and they know neither did A (from its docs or from sources if it’s an open source library). Since they are not even aware of B’s existence, the confusion is next to impossible to clear up.

Therefore, it makes sense that a protected property can only be accessed by a class that owns it (either because it declared it, like with a private property, or because it inherited it, that what makes it different from a private property). Accessing properties of classes that are its siblings and cousins, or potentially could be its siblings or cousins, are not allowed.

2 Likes

Thank you so much!