Protected members not visible in subclasses


#1

Hi! I have an IntList open class with a protected property named items:

open class IntList(array: IntArray, offset: Int = 0, length: Int = array.size) {
    protected var items: IntArray
    var size: Int
        protected set
    val IntList.lastIndex: Int
        get() = size - 1

    init {
        size = array.size
        items = IntArray(size)

        System.arraycopy(array, offset, items, 0, length)
    }

    constructor(vararg array: Int): this(array)
    constructor(list: IntList, offset: Int = 0, length: Int = list.size): this(list.items, offset, length)

    operator fun get(index: Int): Int {
        if (index >= size) throw IndexOutOfBoundsException("index can't be >= size: $index >= $size")
        return items[index]
    }

    fun toArray(): IntArray {
        val array = IntArray(size)
        System.arraycopy(items, 0, array, 0, size)
        return array
    }

    // more stuff
}

and I have a problem with the protected property in a MutableIntList subclass:

class MutableIntList(array: IntArray, offset: Int = 0, length: Int = array.size, ordered: Boolean = true) : IntList(array, offset, length) {
    var ordered = ordered
        private set

    constructor(vararg array: Int): this(array)
    constructor(vararg array: Int, ordered: Boolean): this(array, ordered = ordered)
    constructor(list: IntList, offset: Int = 0, length: Int = list.size): this(list.items, offset, length)

    operator fun set(index: Int, value: Int) {
        if (index >= size) throw IndexOutOfBoundsException("index can't be >= size: $index >= $size")
        items[index] = value
    }
}

In the set function, the class can access its own items without a problem, but in the last constructor (or in any method that receives an instance of an IntList) I got a “Cannot access ‘items’: it is protected in ‘IntList’”.
According to the reference the protected members declared inside a class are visible in subclasses but it seems it’s not visible in instances of the superclass inside the subclass. Which is a bit odd to me.
I don’t want to make items public and despite I can use the toArray function inside the constructor, I would like to know if there’s another way to access the property directly from the subclass and why it works like this.


#2

The current behavior is by design. protected in Java and Kotlin works this way: you can only access a protected member on an object of your type or its subtype. See e.g. https://stackoverflow.com/questions/3071720/why-cant-my-subclass-access-a-protected-variable-of-its-superclass-when-its-i

The workaround is to change items's visibility to internal or public.


#3

My bad. I was sure this was possible in Java because in all my APIs the subclasses were in the same package as the superclass. I thought it was because of being a subclass, not because they were in the same package (I knew protected granted package visibility but always tried to avoid using it).
I blame Java for an access level with no real encapsulation that misinformed me for many years. :laughing:

Thanks! And sorry for the dumb question!


#4

The Java compiler actually performs some tricks with access. You may have seen warnings about synthetic accessors. That is the Java compiler generating a helper method to give you access that the jvm does not provide itself. Of course Kotlin has somewhat different rules.