Внутренний класс Kotlin не может разрешить функцию расширения, объявленную во внешнем классе

#kotlin #kotlin-extension

Вопрос:

Почему внутренний класс в Kotlin не может не получить доступ к функции расширения, объявленной во внешнем классе, как показано ниже:

 class A{
    val a = "as".foo()      // LINE 1
    class B{
        val b = "as".foo()  // LINE 2
    }

    fun String.foo(){}
}
 

Функция LINE 1 расширения разрешена, но LINE 2 функция расширения не разрешена. Интересно, почему существует такое ограничение?

Комментарии:

1. Вы пробовали объявить внутренний класс как inner class ?

Ответ №1:

Это не внутренний класс, потому что вы не использовали в нем ключевое inner слово. Это просто вложенный класс. Если вы знакомы с Java, это похоже на static внутренний класс. Поскольку это не inner так , он не имеет никакой неявной ссылки на внешний класс и не может выполнять голые вызовы членов внешнего класса, поскольку нет конкретного экземпляра для использования членов. Однако он может вызывать членов внешнего класса в экземпляре внешнего класса, поэтому вы можете, например, выполнить следующее:

 class A{
    val a = "as".foo()
    class B{
        val b = A().run { "as".foo() }
    }

    fun String.foo(){}
}
 

Несмотря foo на то, что это функция расширения, она также является членом A из-за того, где она объявлена. Использование функции области, которая делает класс получателем внутри области, является одним из способов вызова одной из функций расширения-члена из другого класса.

ИЗМЕНИТЬ: Вот пример одной из причин, по которой вы хотели бы объявить расширение внутри класса.

 class Sample(val id: Int) {
    private val tag = "Sample#$id"
    fun String.alsoLogged(): String{
        Log.d(tag, this)
        return this
    }
}
 

Вы можете использовать это расширение, чтобы легко регистрировать строки, с которыми вы работаете внутри класса (или когда он является получателем run или apply ). Было бы бессмысленно объявлять вне класса, потому что он использует частную tag собственность этого класса.

Комментарии:

1. Я понимаю, что вы имеете в виду, но я не понимаю, почему они просто не запретили функцию даже объявлять как функцию расширения в классе? Какую пользу может принести функция расширения внутри класса, если ее нельзя использовать вне его области? Я просто пытаюсь понять, что происходило в головах их основных команд, когда они вводили такие ограничения

2. Какие ограничения вы имеете в виду? Такая функция просто имеет два приемника или два this объекта. Это требует и A того, и другого и String должно быть вызвано. Это разрешено, потому что у него есть свои варианты использования. Даже ваш собственный пример предоставил допустимый вариант использования — вы использовали его внутри A as "as".foo() . @Фарид

3. Функция расширения внутри класса может использовать членов этого класса в своей реализации, которая имеет множество вариантов использования. Обычно было бы нецелесообразно объявлять его общедоступным, но есть несколько вариантов использования, например, в DSL. Чаще всего вы объявляете расширение внутри класса, потому что хотите использовать его только внутри этого класса, не загрязняя пространство имен за пределами этого класса, или просто потому, что это приводит к более естественному коду для того, что вы делаете внутри класса с участием некоторых его членов.

Ответ №2:

Это потому, что Котлин компилирует ваш код в

 public final class A {
  @NotNull
  private final Unit a;

  @NotNull
  public final Unit getA() {
    return this.a;
  }

  public final void foo(@NotNull String $this$foo) {
    Intrinsics.checkNotNullParameter($this$foo, "$this$foo");
  }

  public A() {
    this.foo("as");
    this.a = Unit.INSTANCE;
  }
  
  public static final class B {
    public B() {
      // You can't access A's foo() method here.
    }
  }
}