inner class scope issues

J

jrobinss

Hi all,
[SSCCE at end of post]

I don't understand the scope of attributes & methods for an inner
static class. Although I learnt a lot doing the research to write this
message! :)

I'll describe not only what I don't understand, but also what I came
to understand, so it may be of help to others.

I came upon this while defining a different behavior for each instance
of an enum, using methods and attributes from the enum class. I
encountered strange error messages from the compiler.

I reduced the problem to an SSCCE, distinguishing access to private &
protected, and static & non-static, methods of the enum (see SSCCE).
After that I reproduced the SSCCE replacing the enum with a class with
"public static final" attributes. Finally, I reproduced again the
SSCCE without any anonymous class, using an inner class. To my great
satisfaction, I obtained the same results! :)

In that way I eliminated one misinderstanding that I had: I had
overlooked the fact that enums with specialized behavior are
implicitely static anonymous classes. This explained the static access
issues, as well as explaining why I had problems accessing private
stuff: I was in an anonymous sub-class, not in the enum class itself.

A google search has shown my problem is more or less described here:
http://stackoverflow.com/questions/581450/static-context-in-enum-definition
(in particular, in mentions the error messages that bothered me
initially)
BTW, I've also read this
http://java.sun.com/docs/books/jls/third_edition/html/classes.html

I'm providing several SSCCEs. One is with enum, the other is the same
without enums, the last one illustrates the same with only a static
inner class.

There remains one question though:
How come I can access a private method of the super-class using
super.method instead of this.method?
I would have thought I shouldn't be able to access it at all...

For those who wonder (I know this NG!), this is the usage I'm after:
I'm defining a simple command-line interface. The commands are
provided as an enum, which has an abstract "execute" method,
implemented (in an anonymous way) by each instance. Of course, some
commands are simple, but others require help from other classes, and
that's where I started having problems.
The advantage is that I can use all enum utilities, and also that
adding a new command is one step (no switch to modify).

Here's an idea of the code:
public enum MyCommand {
doSomething {
public void execute() {
// ... do something
}
},
doOtherwise {
public void execute() {
// ... do otherwise
}
};
public abstract void execute();
}

Does anyone else use enumerates with behavior to implement command-
line interfaces?
Most examples I've seen use enums for command-line instructions... but
still do a switch. This seems like a shame, when it's possible to
directly code the behavior into the enum!
(in general I tend to regard switching as an bad thing, because it can
be replaced with specialized behavior, aka overriding)

SSCCE (3 of 'em!):
===================================
package enumTests;

/**
* compiler error:
* "The method somePrivateMethod() from the type MyPeople is not
* visible"
*
* previous compile error (for attribute):
* "Cannot make a static reference to the non-static field
* somePrivateAttribute"
* previous compile error (for method):
* "Cannot make a static reference to the non-static method
* somePrivateMethod() from the type EnumTests"
*/
public enum MyPeople {
bob {
private void bobsJob() {
somePrivateStaticMethod();
this.somePrivateMethod(); // compile error
super.somePrivateMethod(); // works fine!
this.someProtectedMethod();
}
};
private void somePrivateMethod() {}
static private void somePrivateStaticMethod() {}
protected void someProtectedMethod() {}
}
===================================
package enumTests;

public class NotAnEnum {
public static final NotAnEnum bob = new NotAnEnum() {
private void bobsJob() {
somePrivateStaticMethod();
this.somePrivateMethod(); // compile error
super.somePrivateMethod(); // works fine!
this.someProtectedMethod();
}
};
private void somePrivateMethod() {}
static private void somePrivateStaticMethod() {}
protected void someProtectedMethod() {}
}
===================================
package enumTests;

public class SomeSuperClass {

public static class SomeInnerClass extends SomeSuperClass {
private void someInnerMethod() {
somePrivateStaticMethod();
this.somePrivateMethod(); // compile error
super.somePrivateMethod(); // works fine!
this.someProtectedMethod();
}
}
private void somePrivateMethod() {}
static private void somePrivateStaticMethod() {}
protected void someProtectedMethod() {}
}
 
J

Joshua Cranmer

jrobinss said:
There remains one question though:
How come I can access a private method of the super-class using
super.method instead of this.method?
I would have thought I shouldn't be able to access it at all...

§ 6.6.1:
Otherwise, if the member or constructor is declared private, then access
is permitted if and only if it occurs within the body of the top level
class (§7.6) that encloses the declaration of the member or constructor.

You can't do the this.method because the subclass doesn't have a copy of
the private method, as private methods are not inherited.
For those who wonder (I know this NG!), this is the usage I'm after:
I'm defining a simple command-line interface. The commands are
provided as an enum, which has an abstract "execute" method,
implemented (in an anonymous way) by each instance. Of course, some
commands are simple, but others require help from other classes, and
that's where I started having problems.

I think my current option setup uses service providers.
 
M

Mark Space

jrobinss said:
Hi all,
[SSCCE at end of post]

I don't understand the scope of attributes & methods for an inner
static class. Although I learnt a lot doing the research to write this
message! :)


I followed the previous conversation and I learned a lot also. I hadn't
really consider that enums might be a static class. Wild, but
apparently true.

However, I think there's still one little thing you are hung up on.
Taking your last SSCCE:

package enumTests;

public class SomeSuperClass {

public static class SomeInnerClass extends SomeSuperClass {
private void someInnerMethod() {
somePrivateStaticMethod();
this.somePrivateMethod(); // compile error
super.somePrivateMethod(); // works fine!
this.someProtectedMethod();
}
}
private void somePrivateMethod() {}
static private void somePrivateStaticMethod() {}
protected void someProtectedMethod() {}
}


Now compare with this:


public class SomeSuperClass {

private void somePrivateMethod() {}

static class StaticNested {
public void staticNestedMethod( SomeSuperClass x ) {
x.somePrivateMethod();
}
}
class InnerClass {
public void innerMethod() {
somePrivateMethod();
}
}
class InnerChildClass extends SomeSuperClass {
public void innerMethod() {
somePrivateMethod();
}
}
static class StaticNestedChildClass extends SomeSuperClass {
public void staticNestedMethod() {
super.somePrivateMethod();
}
}
}

First, all nested (inner classes and static nested classes) have access
to all private members of their enclosing class. This means variables
as well as methods. So where I've written somePrivateMethod() I could
have just as easily put a variable from SomeSuperClass.

The first class, StaticNested, accesses somePrivateMethod through an
instance of SomeSuperClass. Note that this is ANY instance, not just an
enclosing one. Static classes have no enclosing instance, they're
really just two disjoint classes in every way, except that they can
access each other's private members (sounds naughty, eh?). This is a
lot like the friend keyword in C++. Something to keep in mind.

The second class, InnerClass, just accesses somePrivateMethod(). It uses
no qualifiers. Normal scope resolution rules means that the compiler
first checks InnerClass for any matching methods, and finding none
proceed outwards to SomeSuperClass, where it finds the match. This
could cause confusing if field or method names where close in spelling
between an InnerClass and it's outer class. I prefer to design the
names so they are different enough that no confusion occurs.

Next, look at InnerChildClass. Again, no qualifiers are needed.
somePrivateMethod is in scope, and can be called directly without
resorting to the super keyword.

Finally, observe that the StaticNestedChildClass follows much the same
pattern as InnerChildClass. I do find it surprising that super is
allowed here. However, since the private method is in scope (just like
the above three examples) I don't think it should be too surprising.

You can add super to the method call in InnerChildClass:

class InnerChildClass extends SomeSuperClass {
public void innerMethod() {
super.somePrivateMethod();
}
}

And this changes nothing. The "super" is redundant. For
StaticNestedChildClass, the super is required, but otherwise the pattern
is the same. It's a bit like adding "this" whenever a class calls its
own instance methods: redundant, but the compiler accepts it.


Anyway, hopefully approaching it from this direction has helped out
somebody besides me. :)
 
L

Lew

There is no such thing in Java as an "inner static class".
JLS s. 8.5.2
<http://java.sun.com/docs/books/jls/third_edition/html/
classes.html#8.5.2>
The static keyword may modify the declaration of a member
type C within the body of a non-inner class T. Its effect
is to declare that C is not an inner class.

Mark said:
I followed the previous conversation and I learned a lot also.  I hadn't
really consider that enums might be a static class.  Wild, but
apparently true.

JLS s. 8.9:
<http://java.sun.com/docs/books/jls/third_edition/html/
classes.html#8.9>
Nested enum types are implicitly static. It is permissable [sic]
to explicitly declare a nested enum type to be static.

Mark said:
First, all nested (inner classes and static nested classes) have access
to all private members of their enclosing class.  This means variables
as well as methods. ...

There is a subtle consequence of this that came up in another thread.
Consider an inner class that extends its outer class, as it might if
the outer class is abstract with a factory method. An inner-class
method with the same signature as one in its inherited containing
class only overrides the outer/super method if the outer/super method
has at least package-private access. If the outer method has private
access, the inner method does not override it.
 
M

Mark Space

Lew said:
There is a subtle consequence of this that came up in another thread.
Consider an inner class that extends its outer class, as it might if
the outer class is abstract with a factory method. An inner-class
method with the same signature as one in its inherited containing
class only overrides the outer/super method if the outer/super method
has at least package-private access. If the outer method has private
access, the inner method does not override it.


That is a good point. But it also makes sense, I think. Private
methods are never overridden by child classes. It's good to know that
using an inner class doesn't create a loop-hole in this rule. There
could be some bad (thread-safety) consequences if inner classes broke
the rule.
 
M

Mike Schilling

Lew said:
There is a subtle consequence of this that came up in another thread.
Consider an inner class that extends its outer class, as it might if
the outer class is abstract with a factory method. An inner-class
method with the same signature as one in its inherited containing
class only overrides the outer/super method if the outer/super method
has at least package-private access. If the outer method has private
access, the inner method does not override it.

You're right, this is subtle. The rule is

1. Private methods are not virtual and thus can't be overridden.

It's somewhat tempting to think that the rule is

2. Private methods are not visible to descendent classes and thus can't
be overridden.

The fact that inner and nested classes, which *can* see the private methods,
still can't override them demonstrates that the correct rule is 1, not 2.
 
L

Lew

Mike said:
You're right, this is subtle. The rule is

1. Private methods are not virtual and thus can't be overridden.

It's somewhat tempting to think that the rule is

2. Private methods are not visible to descendent classes and thus can't
be overridden.

The fact that inner and nested classes, which *can* see the private methods,
still can't override them demonstrates that the correct rule is 1, not 2.

The exact statement in the JLS, s. 8.2, is
Members of a class that are declared private are not inherited
by subclasses of that class. Only members of a class that are
declared protected or public are inherited by subclasses
declared in a package other than the one in which the class is
declared.
<http://java.sun.com/docs/books/jls/third_edition/html/classes.html#8.2>

Also, s. 8.4.8.1
<http://java.sun.com/docs/books/jls/third_edition/html/classes.html#8.4.8.1>

Your summary (#1) is accurate and succinct.
 
J

jrobinss

Thanks for the long answer, thanks also to Lew. To summarize, the only
question that remained was:
How come I can call a method with super.method() when it's private in
the superclass?
(I had never seen before an access difference between super.method()
and this.method())

Here are my thoughts on the subject...
public class SomeSuperClass {

public static class SomeInnerClass extends SomeSuperClass {
private void someInnerMethod() {
somePrivateStaticMethod();

This works because it's in a static context, so all is ok, and it's a
nested class calling a private member, which is also ok.
this.somePrivateMethod(); // compile error

This doesn't work because it's trying to reach a private method of the
superclass *as though it were inherited*, which it is not. The method
being private, it can't be accessed by a subclass. Of course, the
result is the same without "this.".
(note: there's no overriding here)
More discussion about this case later.
super.somePrivateMethod(); // works fine!

This is the interesting part.

This is ok because...
#1) we need access: SomeInnerClass is a nested class, so access to
private members is ok
#2) we need an instance, because the method is not static (and the
nested class is static): we have super, which is an instance.
So we see that if it wasn't a subclass *and* nested, this wouldn't
work.

I think what gets me wondering is that to understand this, I need to
see SomeInnerClass not as "being a" SomeSuperClass (which is the OO
way of thinking generally advocated), but rather see SomeInnerClass
has having a pointer, called "super", to an instance of
SomeSuperClass.

Let's say it differently: I used to see a subclass as "being a"
superclass, but with no access to superclass's private members. If I
keep this point of view, the access should be ok because it's nested.
So I need to see a subclass as being a superclass, but *without* the
superclass's private members (not with them but with no access). Which
is quite different.

This makes it work out: this.somePrivateMethod() is not ok, because
although I could call somePrivateMethod() on SomeSuperClass, the
method doesn't exist in SomeInnerClass, and being nested doesn't
change anything about its existence.

Note that this works:
((SomeSuperClass)this).somePrivateMethod(); // works fine!

I'm still left wondering if the compiler couldn't have said ok for
this.somePrivateMethod()... #1 and #2 above are both true after all,
I've got an instance, and I've got access to this method because of
nesting.

Still pondering...
 
J

jrobinss

Joshua Cranmer answered
§ 6.6.1:
Otherwise, if the member or constructor is declared private, then access
is permitted if and only if it occurs within the body of the top level
class (§7.6) that encloses the declaration of the member or constructor..

You can't do the this.method because the subclass doesn't have a copy of
the private method, as private methods are not inherited.

This doesn't answer the question: why *can* I access it with
super.method()?
For those who wonder (I know this NG!), this is the usage I'm after:
I'm defining a simple command-line interface. [...]

I think my current option setup uses service providers.

I have no idea what this means. :)
(what do you mean by service providers?)
 
M

Mike Schilling

jrobinss said:
Thanks for the long answer, thanks also to Lew. To summarize, the
only
question that remained was:
How come I can call a method with super.method() when it's private
in
the superclass?
(I had never seen before an access difference between super.method()
and this.method())

The access doesn't differ at all. The point is that the private
method isn't a member of the subclass, but is a member of the
superclass. Conside the following:

class HasInner
{
private String className;

HasInner()
{
className = "HasInner";
}

HasInner(String cn)
{
className = cn;
}

public static void main(String[] args)
{
IsInner ii = new HasInner().new IsInner();
ii.doStuff();
}

private void method()
{
System.out.println(className);
}

class IsInner extends HasInner
{
IsInner()
{
super("IsInner");
};

void doStuff()
{
method();
//this.method();
super.method();
HasInner.this.method();
}
}
}



The output from running this is

HasInner
IsInner
HasInner

The first and third calls use the special access to call the private
method "method" on "this". The second call calls use the special
access to call the private method "method" on the containing instance.
The way to think of "super" is that it it's the same reference as
"this", but it tells the compiler to resolve field and method access
as if it were an instance of the superclass. Except in this odd case
of nested classes, "super" can't access private superclass members,
but neither could any other reference to the superclass.
 
J

Joshua Cranmer

jrobinss said:
This doesn't answer the question: why *can* I access it with
super.method()?

Why wouldn't you be able to? The use of |super| in the method call is
about the selection of the class in the name lookup; in this case, it's
saying, "I want to call the enum's method()." Since you have the access
to do so, the compiler doesn't complain.
I have no idea what this means. :)
(what do you mean by service providers?)

java.util.ServiceLoader.
 
L

Lew

jrobinss said:
How come I can call a method with super.method() when it's private in
the superclass?

Because 'super' means "get at the members through my core that is of
the super-type." Nested classes have access to outer private members,
so the 'super' invocation works. What the nested class instance is
accessing is *its own* code for 'method()', not some other instance's.
Here are my thoughts on the subject...

It is contradictory to name a static nested class with the word
"Inner". In Java, "inner classes" are non-static member classes.
This works because it's in a static context, so all is ok, and it's a
nested class calling a private member, which is also ok.

Correct.


This doesn't work because it's trying to reach a private method of the
superclass *as though it were inherited*, which it is not.

Correct.

The method being private, it can't be accessed by a subclass.

Wrong. It can be accessed if the subclass is nested, as it is here.
Of course, the result is the same without "this.".

Wrong, sort of. The reason it fails without 'this' is that the method
call does not have an owning instance.
This is the interesting part.

This is ok because...
#1) we need access: SomeInnerClass is a nested class, so access to
private members is ok
Correct.

#2) we need an instance, because the method is not static (and the
nested class is static): we have super, which is an instance.

Not completely correct. 'super' is this instance, not just any
instance. 'super' means "'this' taken as an instance of the
supertype".
So we see that if it wasn't a subclass *and* nested, this wouldn't
work.

I think what gets me wondering is that to understand this, I need to
see SomeInnerClass not as "being a" SomeSuperClass (which is the OO
way of thinking generally advocated), but rather see SomeInnerClass
has having a pointer, called "super", to an instance of
SomeSuperClass.

Not really. It's not "an instance", it's "this instance".
Let's say it differently: I used to see a subclass as "being a"
superclass, but with no access to superclass's private members.

A subtype still "is-a" supertype. It is also still true that a nested
subtype does not inherit its supertype's private members.
If I keep this point of view, the access should be ok because it's nested..
So I need to see a subclass as being a superclass, but *without* the
superclass's private members (not with them but with no access).

Not true. The subclass still does have those private superclass
members, it just doesn't inherit them.

The question of access to private members of an outer class is
orthogonal to inheritance, unrelated.
This makes it work out: this.somePrivateMethod() is not ok, because
although I could call somePrivateMethod() on SomeSuperClass, the
method doesn't exist in SomeInnerClass, and being nested doesn't
change anything about its existence.

Wrong. The method does exist in the misnamed 'SomeInnerClass'.
Note that this works:
  ((SomeSuperClass)this).somePrivateMethod(); // works fine!

That's semantically equivalent to calling 'super.somePrivateMethod()'.
I'm still left wondering if the compiler couldn't have said ok for
this.somePrivateMethod()...

No.

#1 and #2 above are both true after all, I've got an instance,
and I've got access to this method because of nesting.

Not enough. You need inheritance, which does not apply to private
members.
 
J

jrobinss

Joshua Cranmer answered me:
java.util.ServiceLoader.

Oh I see, "since Java 6".

I'm with Java 5 (which often leads to an ob-question around these
parts :) )

Thanks for the tip anyway! I'll look into it. For the time being, I
had written myself my own utility CLI class, just to do the output-
input and also the "help" and "exit" commands.
 
J

jrobinss

Thanks for taking time to answer, Lew. Even though I must say I'm
still struggling.

Lew answered:
It is contradictory to name a static nested class with the word
"Inner".  In Java, "inner classes" are non-static member classes.

I know, and I expected your remark. But I wanted to stay consistent
with my first (erroneous) SSCCE.
Wrong, sort of.  The reason it fails without 'this' is that the method
call does not have an owning instance.

This is subtly misleading (I didn't say incorrect, mind you!).
You don't have an instance when you're in a static context. Here there
is an instance which is "this", because the method is not static (even
though the nested class is).
Only, the present instance is an instance of the subclass.
I said your remark wasn't incorrect; indeed, the method doesn't have
an "owning instance" because this particlar instance does not own the
method... but I just wanted to point out the difference with "no
instance at all".
You know, to keep things clear for all the lurkers. :)
Not completely correct.  'super' is this instance, not just any
instance.  'super' means "'this' taken as an instance of the
supertype".

Yes, I expected to be corrected on this. After writing this I tried
out the explicit cast version below, just to verify to myself that I
was going down the wrong path. But I was searching for different
mindsets.

The correct mindset seems to be to think about inheritance of private
members. So, to reformulate...
super.somePrivateMethod(); // works fine!
works because we are first accessing an instance of the nesting class,
then calling a private method upon it.
somePrivateMethod(); // compiler error
doesn't work because we're calling a method that doesn't exist ("this"
does not have this method, so this.somePrivateMethod() doesn't exist).

If this is correct, it means that the compiler error is misleading: it
speaks about an access issue, which taken at face value should be
resolved by nesting. I suppose the compiler simply provides the most
helpful message it can, and the scope-based message is generally the
most helpful.
In this particular case, it's not that helpful to say "you can't
access a private member" when on every other line you are! :)
Not true.  The subclass still does have those private superclass
members, it just doesn't inherit them.

That doesn't help me. It's got them, but doesn't inherit them???
Confusing.
Either a class has a method, or it doesn't.
If it doesn't declare a method, the only way to have it is by
inheritance.

OTOH, it may be just a question of words. Do we really care if some
say...
"it doesn't have a method"
....and others say...
"it has a method but doesn't declare it and can't access it"?
The question of access to private members of an outer class is
orthogonal to inheritance, unrelated.

Well, that's more or less what the whole thread is about. :)
 
L

Lew

Lew answered:
That doesn't help me. It's got them, but doesn't inherit them???
Correct.

Confusing.

But correct.
Either a class has a method, or it doesn't.
Correct.

If it doesn't declare a method, the only way to have it is by
inheritance.
Incorrect.

OTOH, it may be just a question of words.

It's just a question of words that either accurately describe reality or don't.
Do we really care if some say...
"it doesn't have a method"
...and others say...
"it has a method but doesn't declare it and can't access it"?

Yes. It's a difference that accounts for observed behavior.

If the nested subclass instance didn't have the method, then it wouldn't be
able to invoke 'super.method()'. If it inherited the method, then it would be
able to invoke 'this.method()'.
 
M

Mike Schilling

Lew said:
Incorrect.

Here's another semi-obscure case (not compiled, so forgive any typos)

interface Persistent
{
Persistent read(InputStream strm);
void write(OutputStream strm);
}

abstract class DocumentBase implements Persistent
{
protected void writeToFile(String fname) throws IOException
{
FileOutputStream fos = new FileOutputStream(fname);
try
{
write(fos);
}
finally
{
fos.close();
}
}

Observe that DocumentBase has the abstract methods read and write just
as if it had explicitly declared them or inherited them from a
superclass. Obviously, it "inherited" them from the interface, but
that's not usually how we use that word.
 
L

Lew

Here's another semi-obscure case (not compiled, so forgive any typos)

    interface Persistent
    {
            Persistent read(InputStream strm);
            void write(OutputStream strm);
    }

    abstract class DocumentBase implements Persistent
    {
        protected void writeToFile(String fname) throws IOException
        {
            FileOutputStream fos = new FileOutputStream(fname);
            try
            {
                write(fos);
            }
            finally
            {
                fos.close();
            }
    }

Observe that DocumentBase has the abstract methods read and write just
as if it had explicitly declared them or inherited them from a
superclass.  Obviously, it "inherited" them from the interface, but
that's not usually how we use that word.

Yes, it is actually. It is precisely how we use that word.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,744
Messages
2,569,484
Members
44,903
Latest member
orderPeak8CBDGummies

Latest Threads

Top