Reputation: 1749
(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.
Upvotes: 2
Views: 357
Reputation: 37655
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).
Upvotes: 3
Reputation: 280112
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 anExpressionName
. 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);
}
Upvotes: 3
Reputation: 25980
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.
Upvotes: 1