Overwritten method has no access to local variable if it contains field data

Discussion in 'Java' started by Daniel, Jul 22, 2004.

  1. Daniel

    Daniel Guest

    Hi

    Did you know that an overwritten method has no access to local
    variables containing a member field value when method is called from
    the constructor?

    Example: Running this

    import java.awt.event.ActionEvent;
    import javax.swing.AbstractAction;
    public class TestBug {
    private String datum = "Member-String";
    public static void main(String[] args) {
    new TestBug().new TestAction().actionPerformed(null);
    }
    private final class TestAction extends AbstractAction {
    public void actionPerformed(final ActionEvent e) {
    final String localString1 = TestBug.this.datum;
    final String localString2 = "Local-String";
    new Upper() {
    protected void print(String cb) {
    System.out.println(cb + ": " + localString1);
    System.out.println(cb + ": " + localString2);
    }
    }.print("Method");
    }
    }
    }
    class Upper {
    public Upper() {
    print("Constructor");
    }
    protected void print(String calledBy) {
    System.out.println("Upper-String");
    }
    }

    Leads to

    Constructor: null
    Constructor: Local-String
    Method: Member-String
    Method: Local-String

    The localString1 is not accessible from the constructor while
    localString2 is. Do you know a workaround for it? Is it a bug?

    Thanks
    Daniel Frey
     
    Daniel, Jul 22, 2004
    #1
    1. Advertising

  2. Daniel

    David Hilsee Guest

    "Daniel" <> wrote in message
    news:...
    > Hi
    >
    > Did you know that an overwritten method has no access to local
    > variables containing a member field value when method is called from
    > the constructor?
    >
    > Example: Running this
    >
    > import java.awt.event.ActionEvent;
    > import javax.swing.AbstractAction;
    > public class TestBug {
    > private String datum = "Member-String";
    > public static void main(String[] args) {
    > new TestBug().new TestAction().actionPerformed(null);
    > }
    > private final class TestAction extends AbstractAction {
    > public void actionPerformed(final ActionEvent e) {
    > final String localString1 = TestBug.this.datum;
    > final String localString2 = "Local-String";
    > new Upper() {
    > protected void print(String cb) {
    > System.out.println(cb + ": " + localString1);
    > System.out.println(cb + ": " + localString2);
    > }
    > }.print("Method");
    > }
    > }
    > }
    > class Upper {
    > public Upper() {
    > print("Constructor");
    > }
    > protected void print(String calledBy) {
    > System.out.println("Upper-String");
    > }
    > }
    >
    > Leads to
    >
    > Constructor: null
    > Constructor: Local-String
    > Method: Member-String
    > Method: Local-String
    >
    > The localString1 is not accessible from the constructor while
    > localString2 is. Do you know a workaround for it? Is it a bug?


    A non-final method that is called from the constructor and is overridden
    (not "overwritten") in the derived class will be bound to the derived
    class's implementation, even though that derived class's constructor has not
    yet been called. That means that the base object will call a method on an
    derived object that is most likely not in a valid state. It is generally
    considered bad form to do it because it can lead to unexpected behavior. If
    I were to guess at what was happening, I'd say that the compiler handled the
    strings differently because one was a constant, but I really can't say.
    Someone who knows more of the nitpicky details might be able to tell you
    what's going on. I avoid invoking derived class members from base class
    constructors like the plague.

    --
    David Hilsee
     
    David Hilsee, Jul 22, 2004
    #2
    1. Advertising

  3. Daniel

    David Hilsee Guest

    "David Hilsee" <> wrote in message
    news:...
    > > final String localString1 = TestBug.this.datum;
    > > final String localString2 = "Local-String";
    > > new Upper() {
    > > protected void print(String cb) {
    > > System.out.println(cb + ": " + localString1);
    > > System.out.println(cb + ": " + localString2);
    > > }
    > > }.print("Method");


    To elaborate on what I said, I think that the above code was translated into
    something equivalent to this:

    final String localString1 = TestBug.this.datum;
    final String localString2 = "Local-String";

    class Derived extends Upper {
    private final String member = null;
    public Derived() {
    member = localString1;
    }

    protected void print(String cb) {
    System.out.println(cb + ": " + member);
    System.out.println(cb + ": " + "Local-String");
    }
    }

    new Derived().print("Method");

    In the above code, calling print("Constructor") in Upper's constructor would
    invoke Derived.print(String) before the Derived object's constructor had
    run. Therefore, the value of the "member" is null.

    --
    David Hilsee
     
    David Hilsee, Jul 22, 2004
    #3
  4. Daniel

    Daniel Guest

    You're right, it is generally considered bad practice to invoke
    non-final methods from a constructor. However, in this case, the
    problem of overwriding methods used in the constructor takes some new
    (and at least for me: yet unknown) dimensions.

    Interestingly that in JDK 1.5 Beta 2 it seems to work:

    Constructor: Member-String
    Constructor: Local-String
    Method: Member-String
    Method: Local-String

    Daniel Frey
     
    Daniel, Jul 22, 2004
    #4
  5. Daniel

    Daniel Guest

    You're right, it is generally considered bad practice to invoke
    non-final methods from a constructor. However, in this case, the
    problem of overwriding methods used in the constructor takes some new
    (and at least for me: yet unknown) dimensions.

    Interestingly that in JDK 1.5 Beta 2 it seems to work:

    Constructor: Member-String
    Constructor: Local-String
    Method: Member-String
    Method: Local-String

    Daniel Frey
     
    Daniel, Jul 22, 2004
    #5
  6. Daniel

    Chris Uppal Guest

    Daniel wrote:

    > final String localString1 = TestBug.this.datum;
    > final String localString2 = "Local-String";


    Besides the order-of-initialisation-in-constructors issues that have already
    been discussed, the above declarations are much more different than they
    appear.

    The first declares String variable, marks it final, and generates code to
    initialise it to the actual value of TestBug.this.datum at the time of
    execution.

    The second declares a String variable, marks it final, generates code to
    initialise it to "Local-String", AND requires that the compiler repaces all
    references to that variable with "Local-String". I.e. the compiler must not
    generate code that actually uses the variable anywhere (normally -- you can
    still get to it using reflection, JNI, or bytecodes not generated by javac).

    (I have seen your post that says that 1.5 beta behaves differently. I don't
    have time to investigate now, but I'm pretty confident in assuming some sort of
    error -- in your test, in the beta compiler, or possibly in my own
    understanding ;-)

    -- chris
     
    Chris Uppal, Jul 22, 2004
    #6
    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. leo
    Replies:
    1
    Views:
    641
    Adam Maass
    Apr 13, 2005
  2. Tobias Erbsland
    Replies:
    2
    Views:
    337
    Ivan Vecerina
    Dec 13, 2006
  3. macracan
    Replies:
    3
    Views:
    385
    Alf P. Steinbach
    Aug 24, 2007
  4. David Filmer
    Replies:
    19
    Views:
    248
    Kevin Collins
    May 21, 2004
  5. jr
    Replies:
    3
    Views:
    426
Loading...

Share This Page