Subclassing inner class

I have a class with some private fields that I want to access from several other classes, all of which will inherit from one class.

I could provide public accessor methods but I don't feel it is appropriate in this situation so I thought of creating an inner class with protected accessor methods to the private fields.

I would like to subclass this inner class from outside the enclosing class, thus giving access to the private fields without altering the public interface of the enclosing class.

I have written a small sample program to test this idea.

publicclass A{

private String hello ="Hello";

public A(){}

publicclass B{

public B(){}

protected String getString(){

return hello;

}

}

}

publicclass Cextends A.B{

publicstaticvoid main(String[] args){

A a =new A();

A.B c =new C();

}

public C(){

System.out.println(getString());

}

}

However, compiling this gives the error "No enclosing instance of A is in scope" at the constrctor for C.

Am I trying to do something impossible or stupid, or is there a simple solution?

Thanks,

Tristan.

[2359 byte] By [TristanB] at [2007-9-26 4:02:56]
# 1

You need a reference to an instance of class A inside B or C. When you declare an inner class as you did, you can access this reference by A.this, but in this case B cannot be instantiated outside an instance of A.

You want to refer a class A.B without an instance to A. You can do that by declaring B as static. But in this case you don't have access to non-static members of A. So, you still need a reference to A.

Here is a possible solution :

class A {

private String hello = "Hello";

public A() {}

static public class B {

protected A a;

public B(A a){ this.a = a;}

protected String getString() {

return a.hello;

}

}

}

public class C extends A.B {

public static void main(String[] args){

A a = new A();

A.B c = new C(a);

}

public C(A a) {

super(a);

System.out.println(getString());

}

}

It is working.

Anyway the entire story seems too complicated for me :-) Redesigning is not allways a bad idea ...

Regards,

Iulian

iulian_musat at 2007-6-29 13:00:02 > top of Java-index,Archived Forums,Java Programming...
# 2
I had the same problem. I found your entry when I was looking for an answer to my question. It looks like this is logically correct. I had been browsing the bug database for an entry, with no luck so far. I think this is a bug in Java.
cengique at 2007-6-29 13:00:02 > top of Java-index,Archived Forums,Java Programming...
# 3

An explicit reference to an object of the enclosing class must be supplied when the inner class constructor is invoked via super.

class Outer {

class Inner {}

}

//

class C extends Outer.Inner {

C(Outer ref) {

ref.super();

}

//

public static void main(String[] args) {

Outer ref = new Outer();

C c = new C(ref);

}

}

I learned this from The Java Programming Language (Arnold, Gosling, Holmes) 5.2.2 Extending Inner Classes pages 126-127.

marlenemiller at 2007-6-29 13:00:02 > top of Java-index,Archived Forums,Java Programming...
# 4

> An explicit reference to an object of the enclosing

> class must be supplied when the inner class

> constructor is invoked via super.

That's correct, but unfortunately the problem is more than that. Consider the following example of a Test.java:

class Zest {

public String hello = "Hello";

public class Inner {

public String getHello() { return hello; }

}

}

public class Test {

public static void main(String argv[]) {

new Zest().new Inner() {

public String getHello() { return hello; }

};

}

}

Which gives the following compilation error:

Test.java:14: cannot resolve symbol

symbol: variable hello

public String getHello() { return hello; }

Even though "hello" is defined within the enclosing scope of the Zest.Inner class that we're extending.

cengique at 2007-6-29 13:00:02 > top of Java-index,Archived Forums,Java Programming...
# 5
cengique, in your example, do you want Test to extend Zest.Inner?
marlenemiller at 2007-6-29 13:00:02 > top of Java-index,Archived Forums,Java Programming...
# 6
cengique, in your example the instance variable hello is not a member of the class Inner. So it will not be a member of any class that extends Inner.
marlenemiller at 2007-6-29 13:00:02 > top of Java-index,Archived Forums,Java Programming...
# 7

> cengique, in your example the instance variable hello

> is not a member of the class Inner. So it will not be

> a member of any class that extends Inner.

But the variable hello is "accessible" from class Inner. Because it is in the enclosing scope of the class Inner. So if actually the following works:

public class Test {

public static void main(String argv[]) {

new Zest().new Inner().getHello();

}

}

cengique at 2007-6-29 13:00:02 > top of Java-index,Archived Forums,Java Programming...
# 8

1. >>cengique, in your example, do you want Test to extend Zest.Inner?

My error. I see that you are extending Zest.Inner with your anonymous class declaration.

2. The scope of the declaration of the instance variable hello is the body of the class Zest including the nested class Inner. The scope does not include subclasses of Inner.

3. The way I understand/interpret what scope means in the JLS 6.3, the scope of a member of Zest includes subclasses of Zest, but not subclasses of nested classes of Zest.

marlenemiller at 2007-6-29 13:00:02 > top of Java-index,Archived Forums,Java Programming...
# 9

TristanB,

I think cengique's problem might be slightly different from yours. I want to make sure that your question has been answered. When I make a change to the constructor of your example , it compiles and runs.

class A{

private String hello = "Hello";

public A(){}

public class B{

public B(){}

protected String getString(){

return hello;

}

}

}

//

class C extends A.B{

public static void main(String[] args){

A a = new A();

A.B c = new C(a); //added a parameter

}

public C(A a){

a.super(); //added this constructor

System.out.println(getString());

}

}

cengique, would you mind explaining again the problem you are working on?

marlenemiller at 2007-6-29 13:00:02 > top of Java-index,Archived Forums,Java Programming...
# 10
TristanB, the strange syntax ref.super() used to pass a reference to the enclosing class to the inner class from a subclass of the inner class is defined in the JLS 8.8.5.1.I learned more from the discussion in the Java Programming Language than the JLS.
marlenemiller at 2007-6-29 13:00:03 > top of Java-index,Archived Forums,Java Programming...
# 11

A superclass of a subclass needs to be instantiated immediately before the subclass is instantiated.With this im mind, I guess the question here could be to reduced to the problem whether an inner class can be instantiated without the (non-static) enclosing class being instantiated.The answer to the latter must be "no."

fsato4 at 2007-6-29 13:00:03 > top of Java-index,Archived Forums,Java Programming...
# 12

> 2. The scope of the declaration of the instance

> variable hello is the body of the class Zest including

> the nested class Inner. The scope does not include

> subclasses of Inner.

Yes, this is my problem. Logically, the scope should include subclasses of Inner since subclassing extends the scope.

> 3. The way I understand/interpret what scope means in

> the JLS 6.3, the scope of a member of Zest includes

> subclasses of Zest, but not subclasses of nested

> classes of Zest.

Why not? This sounds more and more like a bug to me. Can you find the exact sentence in JLS where this is clarified?

You're right in saying my problem is slightly different than TristanB's. I later found another topic on Advanced Language Topics which seems more relevant:

http://forum.java.sun.com/thread.jsp?forum=4&thread=273816

Unfortunately nothing close in the bug parade. I'm submitting a bug report to get some feedback from Sun.

Thanks..

cengique at 2007-6-29 13:00:03 > top of Java-index,Archived Forums,Java Programming...
# 13

> Can you find the exact sentence in JLS where this is clarified?

JLS 6.3, paragraph 1

The scope of a declaration is the region of the program within which the entity declared by the declaration can be referred to using a simple name (provided it is visible (6.3.1)).

JLS 6.3, paragraph 7

The scope of a declaration of a member m declared in or inherited by a class type C is the entire body of C, including any nested type declarations.

====

As I see it, the scope of a declaration of a member applies to the class where the member is declared and to the classes that inherit the member.

Since a subclass of a nested class does not inherit the members of the enclosing class, a subclass is not in the scope of the declaration of a member of the enclosing class.

marlenemiller at 2007-6-29 13:00:03 > top of Java-index,Archived Forums,Java Programming...
# 14

class Outer {

String hello = "hello";

class Inner {

String s = hello;

}

}

//

class C {

void t() {

new Outer().new Inner() {

};

}

}

>javap -private Outer$Inner

class Outer. Inner extends java.lang.Object

{

private final Outer this$0;

java.lang.String s;

Outer.Inner(Outer);

}

>javap -private C$1

class C$1 extends Outer. Inner {

private final C this$0;

C$1(C,Outer);

}

1. Notice that the inner class Outer$Inner has a reference (Outer this$0) [added by the compiler] to the enclosing class Outer.

2. Notice that the anonymous inner class C$1 has a reference (C this$0) [added by the compiler] to the enclosing class C.

3. The class C$1 does not have a reference to the class Outer. So C$1 cannot access members of Outer by simple name.

marlenemiller at 2007-6-29 13:00:03 > top of Java-index,Archived Forums,Java Programming...
# 15

> 3. The class C$1 does not have a reference to the

> class Outer. So C$1 cannot access members of Outer by

> simple name.

On the contrary, since C$1 inherits all members (including ones added by the compiler) of Outer$Inner, it has the reference to Outer. It can be accessed from C$1 in the following manner: super.this$0 which is illegal since this$0 is hidden from the user (it is internal to the compiler). This demonstrates how JLS is incomplete in defining the behavior in this situation. There should have been a way to access that hidden reference.

cengique at 2007-7-1 10:14:55 > top of Java-index,Archived Forums,Java Programming...
# 16
>On the contrary, since C$1 inherits all members (including ones added by the compiler) of Outer$Inner, it has the reference to Outer. Yes, you are correct. C$1 does have a reference to Outer. My error. However, that reference is private to the superclass Outer.Inner.
marlenemiller at 2007-7-1 10:14:55 > top of Java-index,Archived Forums,Java Programming...
# 17

I have been looking for clues or hints in the Java literature (JLS, white papers, inner class spec and clarifications, Java Spec Report) for a design reason why a subclass of an inner class is not in the scope of a declaration of the enclosing class.

The only (admittedly remote) possibility I could find is this one,

"Within an inner class, all names declared within the enclosing class are said to be in scope -- they can be used as if the inner class code were declared in the outer class." (The Java Programming Language)

Perhaps the code of the subclass is not viewed (perceived, regarded) as if it were declared in the outer class.

marlenemiller at 2007-7-1 10:14:55 > top of Java-index,Archived Forums,Java Programming...
# 18

This is a thought experiment to understand why the scope of a declaration of a member of an enclosing class does not include a subclass of an inner class.

We are the designers of Java. How shall we define the scope of a subclass of an inner class?

1. Warm-up and preparation. Let E be an enclosing class, I be an inner class of E. Let S be a superclass of I. Suppose E and S both declare a field int x. Suppose I references x by simple name. Which x does the compiler choose? We decide: the x in the superclass S shadows the x in the enclosing class. The lesson here is that the superclass has precedence over the enclosing class.

2. Experiment. Let E1, E2, E3 be enclosing classes. Let I1, I2, I3 be inner classes of E1, E2, E3 respectively. Let I2 extend I1 and I3 extend I2. Suppose E1, E2, E3 declare a field x. Suppose I3 references x by simple name. Which x does the compiler choose?

marlenemiller at 2007-7-1 10:14:55 > top of Java-index,Archived Forums,Java Programming...
# 19
errata in reply 18. "How shall we define the scope of a subclass of an inner class?" Sorry, I should have saidHow shall we define the scope of a member of a class?
marlenemiller at 2007-7-1 10:14:55 > top of Java-index,Archived Forums,Java Programming...
# 20

> This is a thought experiment to understand why the

> scope of a declaration of a member of an enclosing

> class does not include a member of an inner class.

>

> We are the designers of Java. How shall we define the

> scope of a subclass of an inner class?

This is a good experiment.

> 1. Warm-up and preparation. Let E be an enclosing

> ...

agreed.

> 2. Experiment. Let E1, E2, E3 be enclosing classes.

> Let I1, I2, I3 be inner classes of E1, E2, E3

> respectively. Let I2 extend I1 and I3 extend I2.

> Suppose E1, E2, E3 declare a field x. Suppose I3

> references x by simple name. Which x does the compiler

> choose?

None. Because there is an ambiguity, isn't there? But this is not a problem, because this kind of situation is already encountered within the known parts of the Java realm. Consider the following example:

$ cat A.java

class A {

int x = 1;

class B {

Object x;

}

class C extends B {

void hello() {

System.out.println("x=" + x);

}

}

}

$ javac A.java

A.java:10: x is inherited from A.B and hides variable in outer class A. An explicit 'this' qualifier must be used to select the desired instance.

System.out.println("x=" + x);

^

1 error

So the compiler already has a way to deal with these kinds of ambiguities. It expects the user to explicitly choose the desired instance of member x. Therefore, it is reasonable to expect that in your example the compiler can force the user to choose the intended member.

cengique at 2007-7-1 10:14:55 > top of Java-index,Archived Forums,Java Programming...
# 21

To recap, I want to summarize my argument. It is a fact that Java has introduced the concept of Inner Classes after its initial JLS publication. Therefore it is reasonable to expect inconsistencies or mistakes both in the specification and the implementations.

However, JLS and the compiler implementations have come a long way to use inner classes efficiently and some of the more complex behaviors previously undefined have been addressed and corrected (see the bug parade on inner class issues). Therefore my conclusion is that there are still some more issues, such as the one discussed here, which simply requires amendments and clarifications to both the JLS and the compiler.

cengique at 2007-7-1 10:14:55 > top of Java-index,Archived Forums,Java Programming...
# 22

1. Thank you for considering my thoughts and point of view.

2. >To recap...

Yes, it could be a bug. I have noticed how the rules in the JLS which are intuitively simple become rather complex in order to accomodate the existence of inner classes. (Then again, there might be a reason -- unknown to me -- for the scope rules as they are. :))

3. I cut and paste your example -- as is -- into my editor. I do not get a compiler error. I am using the SDK 1.4.0 in Windows 2000.

The declaration of x in the superclass B shadows the declaration of x in the enclosing class A.

marlenemiller at 2007-7-1 10:14:55 > top of Java-index,Archived Forums,Java Programming...
# 23

There is something very curious about the fact that I do not get a compiler error for your example.

The Java Programming Language gives an example in 5.2.3 very much like yours and says the use of a simple name is not allowed. Well, I have no problem compiling their example. I sent an errata to Ken Arnold via Sun, but it was returned!

marlenemiller at 2007-7-1 10:14:55 > top of Java-index,Archived Forums,Java Programming...
# 24

> There is something very curious about the fact that I

> do not get a compiler error for your example.

Yes! I was using 1.3.1 compiler on Sun Solaris. I just tried 1.4.1 compiler and I no longer get the error! Weird thing..

> The Java Programming Language gives an example in

> 5.2.3 very much like yours and says the use of a

> simple name is not allowed. Well, I have no problem

> compiling their example. I sent an errata to Ken

> Arnold via Sun, but it was returned!

They seem to have changed somethings in 1.4 but it may take some time for them to publish it in a new JLS.

cengique at 2007-7-1 10:14:55 > top of Java-index,Archived Forums,Java Programming...
# 25
Good luck in presenting your problem/bug to SUN. I hope Gilad Bracha will explain it.
marlenemiller at 2007-7-1 10:14:55 > top of Java-index,Archived Forums,Java Programming...
# 26

> Good luck in presenting your problem/bug to SUN. I

> hope Gilad Bracha will explain it.

:-) Why? What did they tell you? I don't know who that person is..

I already submitted a bug report/support request to Sun on last saturday. I'm still waiting for an answer. Of course I am expecting them to say something like "This is not a bug." ;-)

cengique at 2007-7-1 10:14:55 > top of Java-index,Archived Forums,Java Programming...
# 27

Gilad Bracha is one of the four authors of the second edition of the Java Language Specification. His job title is Computational Theologist. Part of his job seems to be to answer questions about the Java virtual machine and programming language specifications.

I am reading through various sections of the JLS sentence by sentence, word by word. Every now and then I find errors, usually in the examples. If the error seems like something that might also confuse other people, I submit an erratum report to Sun. I always say I do not require a reply, because my findings are rather trivial and definitely not urgent. I think my reports get forwarded to Gilad. His replies are always very intelligent, polite, pleasant and helpful.

(I meant good luck in a positive sense. No sarcasm was intended.)

I enjoyed our discussion. It helps me to try to explain what I think I understand. I like your examples. I never before thought about extending an inner class with an anonymous class.

marlenemiller at 2007-7-1 10:14:55 > top of Java-index,Archived Forums,Java Programming...
# 28

> ... I think my

> reports get forwarded to Gilad. His replies are always

> very intelligent, polite, pleasant and helpful.

That is very nice to hear. Now I'm more hopeful to get an answer.

> (I meant good luck in a positive sense. No sarcasm was

> intended.)

Oh, oops. :) Thanks..

> I enjoyed our discussion. It helps me to try to

> explain what I think I understand. I like your

> examples. I never before thought about extending an

> inner class with an anonymous class.

I enjoyed talking, too. Thanks for all the feedback. I was able to formulate the problem better after this discussion. Maybe it's not a problem after all. I always try to do complicated things which may not really be required. Thanks for taking the time for looking at the stuff I sent.

cengique at 2007-7-1 10:14:55 > top of Java-index,Archived Forums,Java Programming...
# 29

cengique, you might consider submitting your example and questions to the Java Spec Report discussion group at

http://www.ergnosis.com/java-spec-report/java-language/index.html

They could probably explain why your issue is a bug or a feature of the language.

(I learned about (i.e. give credit to) this link from jschell, who contributes many answers and advice on these forums.)

marlenemiller at 2007-7-1 10:14:55 > top of Java-index,Archived Forums,Java Programming...