5

How can I access outer scope from an inner class when I create an extension function for it?

Example

class A {
    inner class B {
        fun own() = this@A
    }
}

This code compiles and executes as it is supposed to.

When I add the following extension function

fun A.B.ext() = this@A

The compilation fails with

Error:(7, 22) Kotlin: Unresolved reference: @A

I read the documentation for qualified this and it briefly mentions extension functions, but without any example.

Is it possible to access outer scope from extension functions?

neshkeev
  • 6,280
  • 3
  • 26
  • 47

1 Answers1

1

An extension function can only do things a non-extension fun ext(x: A.B) can do, so I would expect not, just like you can't access it in Java. This is because it compiles to such a function, the syntax just makes it look like a member.

While class B has a field containing a reference to the outer A instance, this field can't be accessed directly from code by name. Allowing access to it would violate encapsulation.

The linked page talks about "access[ing] this from an outer scope". "Scope" here is used in the sense of https://en.wikipedia.org/wiki/Scope_(computer_science), so in the example you have scopes where the comments say "implicit label"

class A { // outer scope 1
    inner class B { // outer scope 2
        fun Int.foo() { // function scope
        }
    }
}

while

fun A.B.ext() = ...

doesn't have any outer scopes (except for file scope, which doesn't have this). Unless it's really

class C {
    fun A.B.ext() = // can use this@C
}

but you can't write this@A or for that matter this@B because the function isn't defined in the scope of class A or class B.

Alexey Romanov
  • 167,066
  • 35
  • 309
  • 487
  • Thank you for your answer, but what confuses me is that the `own` member function of `B` successfully accesses the outer scope (e.g. in java terms it is `A.this`), but the `ext` extension function is unable to access it. From my perspective they should be the same since the bytecode for the inner (not static) class should have a reference to its outer class. Correct me if I am wrong, I might have misunderstood you. – neshkeev Feb 06 '19 at 10:11
  • Yes, it has a reference to the outer class. But it's a package-level field (not sure why it isn't private) which can be accessed from class methods, but can't be accessed from methods which just take it as an argument. It doesn't matter whether the methods are extension functions. – Alexey Romanov Feb 06 '19 at 10:47
  • Second: the outer scope is not `A.this`, it's `class A { ... }`. Because `A.B.ext()` is not defined inside `class A`, that's not its outer scope. Again, in the documentations example outer scopes for `Int.foo()` are `class A { ... }` and `class B { ... }`, not `Int`. – Alexey Romanov Feb 06 '19 at 10:48
  • Please see https://en.wikipedia.org/wiki/Scope_(computer_science) for meaning of "scope". – Alexey Romanov Feb 06 '19 at 10:53
  • But I define the extension function in the same package. Why can it not access the reference to the outer class? – neshkeev Feb 07 '19 at 09:37
  • That doesn't change anything about scoping, so it still isn't "`this` from outer scope". And the field is compiler-created and can't be accessed by name without reflection from Java or Kotlin (AFAIK). – Alexey Romanov Feb 07 '19 at 09:44
  • And I agree with this comment there: https://stackoverflow.com/questions/1816458/getting-hold-of-the-outer-class-object-from-the-inner-class-object#comment1706317_1816462 – Alexey Romanov Feb 07 '19 at 09:49
  • Please update your answer with the additional information from the comments and I will accept your answer. It would be great if you briefly describe what internal mechanisms extensional functions use for them to work. – neshkeev Feb 07 '19 at 10:08
  • I figured out why the field isn't private (since it was surprising to me) https://stackoverflow.com/questions/54588276/why-is-this0-field-of-an-inner-class-not-private/54596734 – Alexey Romanov Feb 08 '19 at 16:44