-2

Why inner class in Kotlin can't not access extension function declared in outer class as below:

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

    fun String.foo(){}
}

On LINE 1 extension function is resolved but on LINE 2 the function is not resolved. Wonder why there is such limitation?

Farid
  • 2,317
  • 1
  • 20
  • 34

2 Answers2

3

This is not an inner class, because you didn't use the keyword inner on it. It is merely a nested class. If you're familiar with Java, it's like a static inner class. Since it is not inner, it does not have any implicit reference to the outer class, and cannot make bare calls to members of the outer class since there is no specific instance to use the members of. It can however call members of the outer class on an instance of the outer class, so you could for example do the following:

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

    fun String.foo(){}
}

Even though foo is an extension function, it's also a member of A because of where it's declared. Using a scope function that causes a class to be a receiver inside the scope is one way to call one of its member extension functions from another class.

EDIT: Here's an example of one reason you'd want to declare an extension inside a class.

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

You can use this extension to easily log Strings you're working with inside the class (or when it's the receiver of run or apply). It wouldn't make sense to declare outside the class because it uses the private tag property of that class.

Tenfour04
  • 83,111
  • 11
  • 94
  • 154
  • I get what you mean, what I don't get is why didn't they just prohibit a function to be even declared as an extension function in a class? What benefit an extension function inside a class can have if it can't be used outside its scope? I'm just trying to understand what was going through their core teams minds when they put such restrictions – Farid Oct 11 '21 at 13:58
  • What restrictions do you mean? Such function just have two receivers or two `this` objects. It requires both `A` and `String` to be invoked. It is allowed, because it has its use cases. Even your own example provided a valid use case - you used it inside `A` as `"as".foo()`. @Farid – broot Oct 11 '21 at 14:20
  • 2
    An extension function inside a class can use members of that class in its implementation, which has many use cases. It would not be commonly useful to declare it as public, but there are a few use cases, like in a DSL. Most often, you would be declaring an extension inside a class because you only want to use it inside that class without polluting the namespace outside that class, or simply because it leads to more natural code for something you're doing inside the class involving some of its members. – Tenfour04 Oct 11 '21 at 14:32
1

It's because Kotlin compiles your code to

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.
    }
  }
}
jaychang0917
  • 1,880
  • 1
  • 16
  • 21