Первый фрагмент кода (упрощенно):
fun main(args: Array<String>) {
val test = object {
operator fun invoke() = println("test invocation")
}
test()
fun test() = println("test function")
test()
}
Декомпиляция из байт-кода в Java выглядит следующим образом:
public static final void main(@NotNull String[] args) {
Intrinsics.checkParameterIsNotNull(args, "args");
<undefinedtype> test = new Object() {
public final void invoke() {
String var1 = "test invocation";
System.out.println(var1);
}
};
((<undefinedtype>)test).invoke();
<undefinedtype> test$ = null.INSTANCE; // <---
test$.invoke(); // <---
}
Давайте не будем делать выводы слишком рано, и давайте изменим порядок объявлений:
fun main(args: Array<String>) {
fun test() = println("test function")
test()
val test = object {
operator fun invoke() = println("test invocation")
}
test()
}
Это декомпилируется в:
public static final void main(@NotNull String[] args) {
Intrinsics.checkParameterIsNotNull(args, "args");
<undefinedtype> test$ = null.INSTANCE; // <---
test$.invoke();
Object var10000 = new Object() {
public final void invoke() {
String var1 = "test invocation";
System.out.println(var1);
}
};
test$.invoke(); // <---
}
Таким образом, кажется, что функции имеют приоритет над объектами с определенным оператором invoke
, когда они оба объявлены в одной и той же области (область действия класса, область действия функции). Затем давайте явно вызовем оператор invoke
вместо синтаксиса ()
:
fun main(args: Array<String>) {
val test = object {
operator fun invoke() = println("test invocation")
}
fun test() = println("test function")
test.invoke() // calls object's invoke function
test() // calls local function
}
Если вы хотите вызвать функцию invoke
объекта вызова, просто вызовите ее с синтаксисом x.invoke()
. Если вы хотите вызвать функцию, используйте синтаксис ()
.
Другое дело, что когда вы определяете объект / функцию / класс во внутренней области видимости, а во внешней области уже определен объект с таким же именем, происходит дублирование имени. Таким образом, в вашем примере:
fun test() = println("test function")
fun main(args: Array<String>) {
val test = object {
operator fun invoke() = println("test invocation")
}
test() // Prints "test invocation"
}
Локальная переменная test
Функция теней test
определена в области видимости класса. Это может быть лучше видно, если мы просто объявим одно и то же дважды, один раз во внешней области, а затем во внутренней области:
val test = object {
operator fun invoke() = println("test invocation 1")
}
fun main(args: Array<String>) {
val test = object {
operator fun invoke() = println("test invocation 2")
}
test() // Prints "test invocation 2"
}
Здесь это не отличается, если это функция shadowed или свойство. Если внешняя область не является областью класса, я не знаю способа доступа к внешней области. Но если все это происходит внутри класса, тогда все довольно просто:
class SomeClass {
fun test() = println("test function 1")
fun main(args: Array<String>) {
val test = object {
operator fun invoke() = println("test invocation 2")
}
test() // Prints "test invocation 2"
this@SomeClass.test() // Prints "test invocation 1"
}
}
Проблема с объявлением класса, как показано в этом (упрощенном примере):
class test {} // Does not compile
fun test() = println("test function")
Это то, что вы не можете отличить вызов функции от конструкции объекта. Как и в случае объектов с объявленной функцией invoke
, мы можем сделать это, используя синтаксис ()
или invoke()
. Я предполагаю, что если бы было что-то подобное для классов (например, test.contruct()
), выше было бы разрешено, но это не так.
И, наконец, проблема компаньона:
class test {
constructor() {
println("test constructor")
}
companion object {
operator fun invoke() = println("test companion invocation")
}
}
fun main(args: Array<String>) {
test() // Prints: "test constructor"
}
Вы должны помнить, что companion object
- это просто синтаксический сахар. Когда вы объявляете что-либо в компаньоне, а затем получаете доступ к нему по SomeClass.propertyInCompanion
, фактически вы звоните SomeClass.Companion.propertyInCompanion
. Здесь, если есть конфликт, внешний класс всегда побеждает. Если вам нужно вызвать функцию Companion
invoke
, то вы должны указать ее явно:
fun main(args: Array<String>) {
test() // Prints: "test constructor"
test.Companion() // Prints: "test companion invocation"
}
Последние 2 из ваших фрагментов кода представляют собой комбинацию всего вышеперечисленного (затенение имени, внешний класс> сопутствующий) и затенение локальной переменной:
Первый фрагмент:
class test {
constructor() {
println("test constructor")
}
companion object {
operator fun invoke() = println("test companion invocation")
}
}
fun main(args: Array<String>) {
test() // class wins with companion, no local variable introducted
val test = object { // local variable test shadows outer scope "test"
operator fun invoke() = println("test invocation")
}
test() // calls local variable invoke function
fun test() = println("test function") // local function shadows local variable
test() // calls local function
}
Второй фрагмент:
class test {
constructor() {
println("test constructor")
}
companion object {
operator fun invoke() = println("test companion invocation")
}
operator fun invoke() = println("test invocation overload")
}
fun main(args: Array<String>) {
val test = test() // class wins with companion. Also local variable shadows outer scope.
val test1 = test() // calls invoke function of local variable
}
Надеюсь, это ответит на ваш вопрос.