What is the use of invoke operator? Do the parenth...
# getting-started
a
What is the use of invoke operator? Do the parentheses not conflict with any constructors? I couldn't find a lot of context and examples online. Thank you to whoever can explain :)
v
The invoke operator is on instances, the constructor creates an instance, so there should not be a clash?
1
y
And in case of any clash, the constructor takes priority since it is a member while invoke (if defined as an extension) is considered lower priority
1
Also yes constructors use the class name while invoke uses an instance so most of the time there should be no conflict. (except with companion object but IIRC constructor wins in that case unless it is private)
r
Interesting - did the language specifiers consider a compile error from this?
Copy code
class Foo(val string: String) {
  companion object {
    operator fun invoke(string: String): Foo = Foo("changed $string")
  }
}

fun main() {
  println(Foo("initial").string)
}
Without testing it / looking it up I wouldn't know if
initial
or
changed initial
were printed there, so I'd have thought there was a case for prohibiting the clash. Particularly as if you change the constructor to
class private constructor Foo(val string: String)
you actually change the behaviour for all callers, which might produce some unexpected results.
🤔 2
a
@Rob Elliot this snippet is spinning my head haha. I honestly cannnot predict the result without tinkering with it in the IDE
y
I think it's probably the expected behavior since whenever you have an invoke on a companion object, you're intending that to be a secondary constructor, and so I feel like it's intuitive that the primary constructo r of the class would win in the case of identical signatures. In a way, a companion invoke has the same level of weakness as an extension function.
Keep in mind that the IDE will mark the companion invoke as unused, which should be a good sign that something went wrong However, I wouldn't mind if this was made to be a compile-time error whenever the constructor and invoke had identical signatures and the same visibility (while keeping generics in mind of course)
a
Thank you @Vampire and @Youssef Shoaib [MOD] At least I have some clarity on the use of invoke. But I don't think I'll need to use it anytime soon myself.
r
the IDE will mark the companion invoke as unused
It doesn't for me, as it happens. 2021.3.2 IntelliJ IDEA 2021.3.2 (Ultimate Edition) with the Kotlin: 213-1.6.10-release-961-IJ6777.52 plugin.
y
@Rob Elliot Oops, looks like the IDE doesn't mark operators as unused, like ever. It would be a good idea then to throw a warning or error over the compiler invoke case
e
v
Why should it be a compiler error? Even if the constructor is selected, you can still use
Foo.Companion("")
, can you not?
1
e
yes, lots of ways to get to it.
Foo.invoke("")
too
v
And if making the constructor private effects all consumers, well it's just the same as if the implementation of the constructor was changed
y
Yes, maybe a warning is better, but it's just that usually if you have a companion invoke with the exact same signature as a constructor then it can be a bit unexpected. It is a rare edge-case, however, so it might not be that important. Correct me if I'm wrong, but so far does kotlin have any warnings that are based on things that are technically legal and can be useful, but might be unintuitive or unidiomatic? One questionable example is Name Shadowing, where the compiler gives an error for this:
Copy code
fun test(param: Int) {
    var param = param
}
even though it is useful at times.
if making the constructor private effects all consumers, well it's just the same as if the implementation of the constructor was changed
This does have precedent in Kotlin already with member and extension functions having different resolution orders even if they have the same signature, which does result in that "switching of calls" if the member is made private, but does the average user expect the same behaviour from companion invokes? Maybe they should, but do they?