4
votes

I have a private constructor for my class and implemented invoke on the companion object for some kind of "generic constructor"

class Test private constructor(className: String) {
    companion object {
        // If I remove the internal it fails 
        internal inline operator fun <reified T> invoke(): Test {
            return Test(T::class.java.name) // why can I even call it? The constructor is private
        }
    }
}

and I can even have a public inline function that calls that generic constructor

public inline fun test() = Test<Any>() // Why can I call it, it is internal

shouldn't that mean that every invokation test() expands to Test(Any::class.java.name) even though that constructor is private?

So my questions are:

  • Why can that internal inline fun call a private constructor? (a public fun couldn't)
  • Why can that public inline fun call an internal function?
  • And why can I ultimately expose a private constructor in a public inline fun?
2

2 Answers

1
votes
  • Why can that internal inline fun call a private constructor? (a public fun couldn't)

An internal inline function can access non-public API, because it can only be called in the same module, and thus it won't suffer from binary incompatibility that might be introduced when the API changes and the calling module is not re-compiled (see the docs). Such calls are only prohibited for public API inline functions.

  • Why can that public inline fun call an internal function?

It's a bug. Normally, a public inline function cannot access non-public API. This rule seems not to be checked for the invoke() operator calls. I've submitted the issue: KT-20223.

  • And why can I ultimately expose a private constructor in a public inline fun?

That's due to the composition of the expected behavior in the first point and the bug in the second point. Ultimately, it's also a bug.

0
votes

The companion object is within the class. So the code within the class is providing an interface to call the private method, but remember this code is code within the class.

Just as public class methods can access private variables and return their values to the outside, these methods can do this because they are part of the class. The same principle applies in this case, so I suggest this ability to access private members from within the class is to be expected, and behaviour that is less expected is the behaviour of regular inline functions of a class. I believe the behaviour of regular inline functions in this respect is an implementation restriction that simply is not necessary from the companion object.