2

(I keep re-reading that question title and thinking about how ridiculous it must look, but I assure you that is the best description of the problem, and I have an actual application where this is the best structure. I swear I'm not crazy.)

Consider the following. Each block is a separate file:

package myPackage;

public class A {

    public int i;

    public A(int i) {
        this.i = i;
    }

    public class B {

    }
}

package myPackage;

import myPackage.A.B;

public class Main {

    public static void main(String[] args) {
        class C extends B {
            public C(A enclosingInstance) {
                enclosingInstance.super();
            }

            public void show() {
                System.out.println(A.this.i);
            }
        }
        A myA = new A(2);
        C myC = new C(myA);
        myC.show();
    }
}

Note that the enclosingInstance business is to solve a problem involving intermediate constructor invocations. See "Why can't outer classes extend inner classes?".

I would expect the output to be "2". But instead, I have a compile error on System.out.println(A.this.i);:

No enclosing instance of the type A is accessible in scope

I think the programmatic concept I'm trying to solve is sound: Create a new type of B inside main to give to A that uses things from A that types of B can access.

So what am I doing wrong, or why isn't this possible in java?

EDIT/UPDATE: Note that the same error appears when the code in main is moved to a non-static method. That is to say, I tried moving everything inside of static void main to a new, non-static method of class Main called go(). Then I changed static void main to the single line new Main().go();. The error is in the same spot. So it doesn't seem to be an issue of class C being defined in a static context.

14
  • @snickers10m does B use non-static attributes of A ? Or private ones (static or not) ? Commented Oct 9, 2015 at 0:51
  • @Dici B doesn't use anything from A - there's no code at all in B. However C, which is a type of B, uses the non-static, public instance variable of A, i. Commented Oct 9, 2015 at 0:53
  • Not sure I agree with the duplicate, but anyway. A simple fix is to define a getOuterInstance() on B which just returns A.this Commented Oct 9, 2015 at 0:54
  • @snickers10m ok, so I think this is bad design. It's like you're trying to break encapsulation by exposing internal functionalities to external classes Commented Oct 9, 2015 at 0:56
  • @Dici That works, but it's kind of a hack. I'm hoping for an actual solution that doesn't add complicated structure. I also disagree with the duplicate, but I'm slowly trudging through the duplicate question to see what I can learn. Commented Oct 9, 2015 at 0:56

3 Answers 3

3

You want A.this to refer to the enclosing instance of the B instance. But why should it? That's not what the syntax means. A.this would mean the enclosing A instance of the C instance, and this does not make sense because C is not an inner class of A.

To make this clearer, here is an example where C is an inner class of A.

public class A {

    public int i;

    public A(int i) {
        this.i = i;
    }

    public class B {
        void foo() {
            System.out.println(A.this.i);
        }
    }

    public class C extends B {
        C(A a) {
            a.super();
        }
        void bar() {
            System.out.println(A.this.i);
        }
    }

    public static void main(String[] args) {
        A a1 = new A(1);
        A a2 = new A(2);
        C c = a1.new C(a2);
        c.foo();
        c.bar();
    }
}

Here C extends B, and both C and B are inner classes of A. Therefore any C has an enclosing A instance, and it also has an enclosing A instance when considered as a B, and these enclosing instances are different (as proved by the fact that foo and bar print different numbers).

So, A.this could not possibly mean what you want it to mean, because it already means something else. I guess the reason why the language designers didn't come up with other syntax to mean the enclosing instance of a super class, is because such syntax would be very complicated, with little pay-off (simple workarounds already exist).

Sign up to request clarification or add additional context in comments.

11 Comments

So basically, in my original code, .this could refer to two different places: Main because it's a local class of Main, and A because it's a subclass of an inner class of Main, and the Java developers simply said "Only one enclosing instance per class definition"?
It's just that C is not an inner class of A, so there is no notion of enclosing instance. That's what I get from the answer, and that's true
@snickers10m There's not only one enclosing instance per class definition, because inner classes can have inner classes. So you could have D inside C inside B inside A, and any D would have 3 enclosing instances (an A, a B and a C). Add in the fact that any of these classes could have super classes with many enclosing instances, and the whole thing becomes absurd. It's just not sensible for the language to allow you to refer to all these things.
@PaulBoddington Okay. I was just checking my understanding. Thank you for the answer!
@PaulBoddington So, just to check, what's the rule of thumb for determining the enclosing instance for a class? Is it always the top-level class of the file the class was defined in? Or is it just the next-level-up class? (So in the case of D inside C inside B inside A, would D's enclosing instance be C or A?)
|
3

This is absurd code that you should never write for production.

It is, in part, explained in the documentation for Explicit Constructor Invocations

Qualified superclass constructor invocations begin with a Primary expression or an ExpressionName. They allow a subclass constructor to explicitly specify the newly created object's immediately enclosing instance with respect to the direct superclass (§8.1.3). This may be necessary when the superclass is an inner class.

All this to say that C is a local class (which is an inner class, which is kind of nonsense because if you declare it in a static method there is no enclosing instance) that is a subclass of B but not a nested class of A. As such, there is no enclosing instance. An instance of C does not have an enclosing instance. (Though it would if you declared it in an instance method, but that would be an instance of Main.)

The newly created object's immediately enclosing instance (from JLS) is specified indirectly through a constructor parameter.

You'd have to store it yourself

private A enclosingInstance;
public C(A enclosingInstance) throws CloneNotSupportedException {
    enclosingInstance.super();
    this.enclosingInstance = enclosingInstance;
}

and since A#i is public, you can access it normally

public void show() {
    System.out.println(enclosingInstance.i);
}

8 Comments

If you look at my edit, I say that I tried moving my code into an instance method in Main called go(), and changed static void main to new Main().go(). The error is in the exact same spot. So it doesn't have to do with being defined in a static context.
@snickers10m If you reread my answer, putting the code in an instance method doesn't change anything in relation to A (or B), it just makes an instance of Main available to C. You still need to manually capture the instance of A.
@snickers10m No need to apologize, this is weird code. Please don't ever use it.
But why? I think it's quite elegant. Sure it's hard to understand at first but it's simple, compared to storing the enclosing instance in a variable.
If it's hard to read/understand, it is not elegant to any but one person: the author.
|
1

With the provided information, I would do this :

public class B {
    protected A getOuterInstance() {
        return A.this;
    }
}

and just let C inherit and use this method. I know you dislike this method but this is the simplest answer I can see. With more information, I would probably propose a design which would try not involving any inner class as this is not a normal use case for inner classes.

1 Comment

It's not that I dislike this solution, it's just that it's a workaround rather than a fix. It doesn't address the error about an enclosing instance not being available in scope, it just sidesteps it. I think it's good for you to post this answer - it will be helpful to people. However, I'm not going to make it the accepted answer since it's just a workaround.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.