Access protected member of another instance of a superclass

I have the class O that defines the protected field code. I have the class BO that extends O.

In a method of BO that takes a BO as a parameter, I can access this.code and other.code.

But in a method of BO that takes an O as a parameter, I can access this.code but not other.code.

Why ?

Can you post some code?

private val EMPTY_CODE: Code = emptyList()

public open class OrderBitField internal constructor(protected val code: Code): OrderField<OrderBitField> {
    init {
        require(code.isNotEmpty()) { "code must not be empty (internal error)" }
    }

    override fun compareTo(other: OrderBitField): Int {
        val n = code.size.coerceAtMost(other.code.size)
        for (i in 0..<n) {
            val diff = code[i].compareTo(other.code[i])
            if (diff != 0) return diff
        }
        return code.size.compareTo(other.code.size)
    }

    override fun rPad(toSize: UInt): OrderBitField {
        val uSize = code.size.toUInt()
        if (toSize == uSize) return this
        require(toSize > uSize) { "toSize must be greater or equal to the current size" }
        return OrderBitField(code + (List((toSize - uSize).toInt()) { 0u.toUByte() }))
    }
}

public class BoundedOrderBitField internal constructor(
    code: Code, override val maxSize: UInt
): OrderBitField(code), BoundedOrderField<OrderBitField, BoundedOrderBitField> {
    init {
        require(code.size.toUInt() <= maxSize) { "the size of the code must not exceed maxSize (internal error)" }
    }

    override fun rPad(toSize: UInt): BoundedOrderBitField {
        require(toSize <= maxSize) { "toSize must not exceed maxSize" }
        val uSize = code.size.toUInt()
        if (toSize == uSize) return this
        require(toSize > uSize) { "toSize must be greater or equal to the current size" }
        return BoundedOrderBitField(code + (List((toSize - uSize).toInt()) { 0u.toUByte() }), maxSize)
    }
    override fun rPad(): BoundedOrderBitField = rPad(maxSize)

    override operator fun plus(other: BoundedOrderBitField): BoundedOrderBitField {
        val code = rPad().code + other.code
        val newMaxSize = maxSize + other.maxSize
        return BoundedOrderBitField(code, newMaxSize)
    }
    override operator fun plus(other: OrderBitField): OrderBitField {
        val code = rPad().code + other.code
        return OrderBitField(code)
    }
}

In the last method, other.code is invalid.

Tbh I have no idea how either function works. I’m surprised that you can access other.code in the plus function that takes a BoundedOrderBitField.

In Java at least, the logic is that visibility is per class and not per instance. So any method inside a given class’s body has access to all the private members of the class and of its nested or inner classes(*), and all the protected members of the class and its ancestors, regardless of which instance it’s accessed on. (And in Java, “protected” includes the rest of the package, but that at least is not true in Kotlin.)

(*)That particular rule does not hold in Kotlin, as said here :

In Kotlin, an outer class does not see private members of its inner classes.

And from the formulation of the rest of the article, I see no reason to depart from Java’s interpretation of visibility applying to all instances. In any case, the fact that it works in the first `plus` function should be an indication that it’s not per-instance.

This hierarchy seems a bit complicated to be able to reproduce some issue.

Also, tried to run this code on https://play.kotlinlang.org and it does not compile, Code and OrderField seem not to be present.

They’re not, but that’s not the point. The `code` field is introduced by the first class and the issue happens when accessing it from the second class.

Here’s a smaller example reproducing the issue:

open class Base(protected val value: Int)

class Derived(value: Int): Base(value) {
    fun f1(other: Derived): Derived = Derived(other.value) // OK

    fun f2(other: Base): Base = Base(other.value) // Cannot access 'val value: Int': it is protected in '/Base'.
}

Obviously this behavior is different from Java and it’s not clear from the documentation why it works like this.

There has been some previous discussions about this:
Protected members not visible in subclasses
Cannot access protected method declared in super class

2 Likes