JavaC and Jikes both violate JLS calling super of inner class

Discussion in 'Java' started by Mike Schilling, Sep 26, 2003.

  1. Take the following code:

    public class Outer {
    String className;

    public static void main(String[] args) {
    Outer amb = new Outer();
    SubOuter sa = amb.new SubOuter();
    SubOuter.SubInner subIn = sa.new SubInner();
    System.out.println(subIn.getSuperEncloserName());
    }

    Outer() {
    className = "Outer";
    }

    class Inner {
    String getEncloserName() {
    return className;
    }
    }

    class SubOuter extends Outer {
    SubOuter() {
    className = "SubOuter";
    }

    class SubInner extends Inner {

    SubInner() {
    super(); // ******
    }

    String getSuperEncloserName() {
    return super.getEncloserName();
    }
    }
    }
    }

    When compiled with either javac or jikes and then run, the output is
    "SubOuter".

    Let's analyze what happens at the starred line. We must determine the
    immediately enclosing instance of "this" with respect to class Inner.
    "this", an instance of SubInner, has an immediately enclosing instance
    SubOuter.this, which in turn has an immediately enclosing instance
    Outer.this. According to JLS 8.8.5.1:

    Let O be the innermost lexically enclosing class of which S is a member,
    and let n be an integer such that O is the nth lexically enclosing class
    of C.
    The immediately enclosing instance of i with respect to S is the nth
    lexically
    enclosing instance of this.

    Here, S is Inner, O Outer, and C SubInner. By this rule, the enclosing
    instance should be Outer.this (the 2nd lexically enclosing instance of
    "this"), since the type of the selected enclosing instance of "this" must
    exactly match the innermost lexically enclosing class of S. As running the
    code shows, the enclosing instance is actually SubOuter.this (the first
    lexically enclosing instance of "this".) This is what I would have naively
    assumed: that the enclosing instance of "this" needs to be assignment
    compatible with the enclosing class of S, not that it needs to be identical.
    In other words, one would expect the following to compile correctly:

    public class Outer2 {
    class Inner {
    }
    }

    public class SubOuter2 extends Outer2 {
    class SubInner extends Outer2.Inner {
    SubInner() {
    super();
    }
    }
    }

    In fact it does. Qualifying the super call as "Outer2.this.super()" is not
    required.

    Thus, 8.8.5.1 should read something like:

    Let O be the innermost lexically enclosing class of which S is a member,
    and let n be the smallest integer such that O or a subclass of O is the
    nth
    lexically enclosing class of C
    The immediately enclosing instance of i with respect to S is the nth
    lexically
    enclosing instance of this.

    This matches both our expectations and what seems to be implemented by javac
    and jikes. Note that a similar correction is needed in 15.9.2, which
    describes the behavior of the constructor of an anonymous class whose
    superclass in an inner class.
    Mike Schilling, Sep 26, 2003
    #1
    1. Advertising

  2. Mike Schilling

    Steven Coco Guest

    Mike Schilling wrote:

    > Take the following code:


    I'm sorry: I didn't completely read your synopsis of the Lang spec.
    What is it that seems wrong to you--the output is correct.

    --

    ..Steven Coco.
    .........................................................................
    When you're not sure:
    "Confess your heart" says the Lord, "and you'll be freed."
    Steven Coco, Sep 27, 2003
    #2
    1. Advertising

  3. "Steven Coco" <> wrote in message
    news:gN6db.7779$...
    > Mike Schilling wrote:
    >
    > > Take the following code:

    >
    > I'm sorry: I didn't completely read your synopsis of the Lang spec.
    > What is it that seems wrong to you--the output is correct.
    >


    The spec says that the type for enclosing instance for the superclass must
    exactly match the type of an enclosing instance of the subclass. That isn't
    what's happening.
    Mike Schilling, Sep 27, 2003
    #3
  4. Mike Schilling

    Steven Coco Guest

    OK. I see where you're at. You said:

    > As running the code shows, the enclosing instance is actually
    > SubOuter.this


    It boils down to this:

    In this bit of code:

    > SubOuter() {
    > className = "SubOuter";
    > }


    *I* ... naively assumed that the variable className would refer to the
    member of the inner class's enclosing instance--because it is lexically
    defined that way [just look up the page 21 lines] ~ sorry.

    But I am IN LUCK! BECAUSE;

    section 8.1.5 of the spec says (and I quote! ;) ):

    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.

    I _do_ hope we could still be friends! Please see below.

    Thank you, and God bless you.

    But just in case I would leave you on a hook, I think what you want to
    do is use "this.className = "SubOuter";".

    --

    ..Steven Coco.
    .........................................................................
    Jesus saves.
    Steven Coco, Sep 28, 2003
    #4
  5. Well, the confusion here is what exactly super.getEncloserName() does.
    It does call the inheritted (as oppoused to the overriden method).
    But it does not change 'this' in any way.

    To make it clear do the following:
    1. Override getEncloserName() to return some other fixed value
    2. Execute the code again

    Now you see what is going on - you call the Inner.getEncloserName instead of
    the SubInner.getEncloserName.
    But what is 'this' during this call? Normally, it is a SubInner object, so
    the value of className there is "SubOuter".

    Regards,
    Dobromir

    "Mike Schilling" <> wrote in message
    news:Qt0db.53$...
    > Take the following code:
    >
    > public class Outer {
    > String className;
    >
    > public static void main(String[] args) {
    > Outer amb = new Outer();
    > SubOuter sa = amb.new SubOuter();
    > SubOuter.SubInner subIn = sa.new SubInner();
    > System.out.println(subIn.getSuperEncloserName());
    > }
    >
    > Outer() {
    > className = "Outer";
    > }
    >
    > class Inner {
    > String getEncloserName() {
    > return className;
    > }
    > }
    >
    > class SubOuter extends Outer {
    > SubOuter() {
    > className = "SubOuter";
    > }
    >
    > class SubInner extends Inner {
    >
    > SubInner() {
    > super(); // ******
    > }
    >
    > String getSuperEncloserName() {
    > return super.getEncloserName();
    > }
    > }
    > }
    > }
    >
    > When compiled with either javac or jikes and then run, the output is
    > "SubOuter".
    >
    > Let's analyze what happens at the starred line. We must determine the
    > immediately enclosing instance of "this" with respect to class Inner.
    > "this", an instance of SubInner, has an immediately enclosing instance
    > SubOuter.this, which in turn has an immediately enclosing instance
    > Outer.this. According to JLS 8.8.5.1:
    >
    > Let O be the innermost lexically enclosing class of which S is a

    member,
    > and let n be an integer such that O is the nth lexically enclosing

    class
    > of C.
    > The immediately enclosing instance of i with respect to S is the nth
    > lexically
    > enclosing instance of this.
    >
    > Here, S is Inner, O Outer, and C SubInner. By this rule, the enclosing
    > instance should be Outer.this (the 2nd lexically enclosing instance of
    > "this"), since the type of the selected enclosing instance of "this" must
    > exactly match the innermost lexically enclosing class of S. As running

    the
    > code shows, the enclosing instance is actually SubOuter.this (the first
    > lexically enclosing instance of "this".) This is what I would have

    naively
    > assumed: that the enclosing instance of "this" needs to be assignment
    > compatible with the enclosing class of S, not that it needs to be

    identical.
    > In other words, one would expect the following to compile correctly:
    >
    > public class Outer2 {
    > class Inner {
    > }
    > }
    >
    > public class SubOuter2 extends Outer2 {
    > class SubInner extends Outer2.Inner {
    > SubInner() {
    > super();
    > }
    > }
    > }
    >
    > In fact it does. Qualifying the super call as "Outer2.this.super()" is

    not
    > required.
    >
    > Thus, 8.8.5.1 should read something like:
    >
    > Let O be the innermost lexically enclosing class of which S is a

    member,
    > and let n be the smallest integer such that O or a subclass of O is

    the
    > nth
    > lexically enclosing class of C
    > The immediately enclosing instance of i with respect to S is the nth
    > lexically
    > enclosing instance of this.
    >
    > This matches both our expectations and what seems to be implemented by

    javac
    > and jikes. Note that a similar correction is needed in 15.9.2, which
    > describes the behavior of the constructor of an anonymous class whose
    > superclass in an inner class.
    >
    >
    Dobromir Gaydarov, Sep 28, 2003
    #5
  6. "Steven Coco" <> wrote in message
    news:V7xdb.11438$...
    > OK. I see where you're at. You said:
    >
    > > As running the code shows, the enclosing instance is actually
    > > SubOuter.this

    >
    > It boils down to this:
    >
    > In this bit of code:
    >
    > > SubOuter() {
    > > className = "SubOuter";
    > > }

    >
    > *I* ... naively assumed that the variable className would refer to the
    > member of the inner class's enclosing instance--because it is lexically
    > defined that way [just look up the page 21 lines] ~ sorry.
    >
    > But I am IN LUCK! BECAUSE;
    >
    > section 8.1.5 of the spec says (and I quote! ;) ):
    >
    > 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.
    >
    > I _do_ hope we could still be friends! Please see below.
    >
    > Thank you, and God bless you.
    >
    > But just in case I would leave you on a hook, I think what you want to
    > do is use "this.className = "SubOuter";".


    Try it; the behavior won't change. If a field name could either refer to a
    field inherited from a superclass or a field from an enclosing instance, the
    superclass wins.
    Mike Schilling, Sep 28, 2003
    #6
  7. "Dobromir Gaydarov" <> wrote in message
    news:...
    > Well, the confusion here is what exactly super.getEncloserName() does.
    > It does call the inheritted (as oppoused to the overriden method).
    > But it does not change 'this' in any way.


    Precisely. If a class has one or more inner classes in its inheritnace
    chain, there is an innermost enclosing instance defined with respect to each
    ancestor class which is an inner class. The method "getEncloserName" is
    intended to find out what the enclosing instance with respect to the class
    "Inner" is.
    >
    > To make it clear do the following:
    > 1. Override getEncloserName() to return some other fixed value
    > 2. Execute the code again
    >
    > Now you see what is going on - you call the Inner.getEncloserName instead

    of
    > the SubInner.getEncloserName.
    > But what is 'this' during this call? Normally, it is a SubInner object, so
    > the value of className there is "SubOuter".


    But that's not how it works. Field names are bound at compile time, not run
    time. "className" means "the field className in the type of 'Inner', as
    determined by the compiler." In this case it resolves to
    this.Outer.className. The fact that the example prints "SubOuter" proves
    that this.Outer.className is a SubOuter, in violation of the JLS.
    Mike Schilling, Sep 28, 2003
    #7
  8. "Steven Coco" <> wrote in message
    news:V7xdb.11438$...
    > OK. I see where you're at. You said:
    >
    > > As running the code shows, the enclosing instance is actually
    > > SubOuter.this

    >
    > It boils down to this:
    >
    > In this bit of code:
    >
    > > SubOuter() {
    > > className = "SubOuter";
    > > }

    >
    > *I* ... naively assumed that the variable className would refer to the
    > member of the inner class's enclosing instance--because it is lexically
    > defined that way [just look up the page 21 lines] ~ sorry.


    No, it refers to this.className. Names inherited from ancestors hide names
    inherited from enclosing instances.

    >
    > But I am IN LUCK! BECAUSE;
    >
    > section 8.1.5 of the spec says (and I quote! ;) ):
    >
    > 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.
    >

    Yes, the name is still in scope, but hidden by the scope of the superclass.

    > I _do_ hope we could still be friends! Please see below.
    >
    > Thank you, and God bless you.
    >
    > But just in case I would leave you on a hook, I think what you want to
    > do is use "this.className = "SubOuter";".


    Try it. Same result. In fact, same generated bytecode.
    Mike Schilling, Sep 28, 2003
    #8
  9. Mike Schilling

    Neal Gafter Guest

    Mike Schilling wrote:
    > Let's analyze what happens at the starred line. We must determine the
    > immediately enclosing instance of "this" with respect to class Inner.
    > "this", an instance of SubInner, has an immediately enclosing instance
    > SubOuter.this, which in turn has an immediately enclosing instance
    > Outer.this. According to JLS 8.8.5.1:
    >
    > Let O be the innermost lexically enclosing class of which S is a member,
    > and let n be an integer such that O is the nth lexically enclosing class
    > of C.
    > The immediately enclosing instance of i with respect to S is the nth
    > lexically
    > enclosing instance of this.
    >
    > Here, S is Inner, O Outer, and C SubInner. By this rule, the enclosing
    > instance should be Outer.this (the 2nd lexically enclosing instance of
    > "this"), since the type of the selected enclosing instance of "this" must
    > exactly match the innermost lexically enclosing class of S.


    No, it need not exactly match. It is the innermost lexically exclosing class of
    which S is a member. SubOuter is indeed a lexically enclosing class of which
    SubInner is a member. In fact, it is an inherited member.
    Neal Gafter, Sep 28, 2003
    #9
  10. Mike Schilling

    Steven Coco Guest

    Mike Schilling wrote:

    >>But just in case I would leave you on a hook, I think what you want to
    >>do is use "this.className = "SubOuter";".

    >
    > Try it. Same result. In fact, same generated bytecode.


    Oops! That's still right since there is no instance of Outer other than
    the one you instantiated.

    --

    ..Steven Coco.
    .........................................................................
    When you're not sure:
    "Confess your heart" says the Lord, "and you'll be freed."
    Steven Coco, Sep 28, 2003
    #10
  11. "Neal Gafter" <> wrote in message
    news:...
    > Mike Schilling wrote:
    > > Let's analyze what happens at the starred line. We must determine the
    > > immediately enclosing instance of "this" with respect to class Inner.
    > > "this", an instance of SubInner, has an immediately enclosing instance
    > > SubOuter.this, which in turn has an immediately enclosing instance
    > > Outer.this. According to JLS 8.8.5.1:
    > >
    > > Let O be the innermost lexically enclosing class of which S is a

    member,
    > > and let n be an integer such that O is the nth lexically enclosing

    class
    > > of C.
    > > The immediately enclosing instance of i with respect to S is the nth
    > > lexically
    > > enclosing instance of this.
    > >
    > > Here, S is Inner, O Outer, and C SubInner. By this rule, the enclosing
    > > instance should be Outer.this (the 2nd lexically enclosing instance of
    > > "this"), since the type of the selected enclosing instance of "this"

    must
    > > exactly match the innermost lexically enclosing class of S.

    >
    > No, it need not exactly match. It is the innermost lexically exclosing

    class of
    > which S is a member. SubOuter is indeed a lexically enclosing class of

    which
    > SubInner is a member. In fact, it is an inherited member.


    S is Inner, not SubInner, so I presume you meant to say "SubOuter is indeed
    a lexically enclosing class of which Inner is a member. In fact, it is an
    inherited member." And this is inded the answer. As 8.2 makes clear, the
    members of a class type include members inherited from the direct
    superclass. I was missing the fact that this applies to member classes as
    well as fields and methods. (The fact that java.util.Class defines both
    getClasses() and getDeclaredClasses() methods should have been a clue.)

    Thanks,
    Mike
    Mike Schilling, Sep 29, 2003
    #11
    1. Advertising

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

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Alex Polite
    Replies:
    14
    Views:
    946
  2. Michael Wurm
    Replies:
    12
    Views:
    579
    Roedy Green
    Jul 23, 2004
  3. Guest

    super.super.super how?

    Guest, Feb 19, 2005, in forum: Java
    Replies:
    24
    Views:
    10,737
    Darryl Pierce
    Feb 24, 2005
  4. Ninan
    Replies:
    3
    Views:
    272
    Jack Klein
    Mar 3, 2006
  5. fi3nd
    Replies:
    0
    Views:
    371
    fi3nd
    Apr 8, 2010
Loading...

Share This Page