#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.
}
}
}