Swing uses bad features

  • Follow


Is it just me, or does Swing do things in a bad way?

For example, if I subclass a Swing class, that superclass's
constructor will be executed as the implicit first statement
in my constructor. This is as all Java constructors work.

However, many Swing constructors will call setFont(),
setForeground(), and setBackground().

JLabel's constructor also calls setText().

Now this is all done BEFORE any of my constructor's
statements are executed. However, due to  polymorphism,
if I override any of the above-named methods, it is
MY method that gets executed.

Thus if I subclass JLabel, for examle, and I override
setText(), my setText() method gets called BEFORE I can
initialize anything. But my setText() method might depend
on something I create in my constructor - which hasn't been
executed yet.

In my opinion, JLabel should not call setText() in its constructor.
If it really wants to do that, it should do something like:
   ((JLabel)this).setText(...);
so that it calls its own setText(), not my overriding method.

One place that this causes problems is in a composite component.
Suppose my subclass of JPanel creates 5 other widgets.
Now suppose that I want to reset the background of all of these
components when the user invokes my setBackground() method.
So in my setBackground() i do this:
   w1.setBackground(color);
   w2.setBackground(color);
etc.

But my setBackground() method gets called by JPanel's
constructor BEFORE my constructor has created any of these widgets!

-- 
Fred L. Kleinschmidt
Boeing Associate Technical Fellow
Technical Architect, Software Reuse Project





0
Reply Fred 6/5/2006 7:41:40 PM

> One place that this causes problems is in a composite component.
> Suppose my subclass of JPanel creates 5 other widgets.
> Now suppose that I want to reset the background of all of these
> components when the user invokes my setBackground() method.
> So in my setBackground() i do this:
>   w1.setBackground(color);
>   w2.setBackground(color);
> etc.
>
> But my setBackground() method gets called by JPanel's
> constructor BEFORE my constructor has created any of these widgets!

if you want your 5 widgets to have same background as JPanel,
then set their background to null (for AWT) or make them not opaque (for 
swing).
Otherwise in setBackground check if your widgets are not null.

Andrey

-- 
http://uio.imagero.com Unified I/O for Java
http://reader.imagero.com Java image reader
http://jgui.imagero.com Java GUI components and utilities


0
Reply Andrey 6/5/2006 8:42:53 PM


"Andrey Kuznetsov" <spam0@imagero.com.invalid> wrote in message 
news:e62504$k91$1@online.de...
>> One place that this causes problems is in a composite component.
>> Suppose my subclass of JPanel creates 5 other widgets.
>> Now suppose that I want to reset the background of all of these
>> components when the user invokes my setBackground() method.
>> So in my setBackground() i do this:
>>   w1.setBackground(color);
>>   w2.setBackground(color);
>> etc.
>>
>> But my setBackground() method gets called by JPanel's
>> constructor BEFORE my constructor has created any of these widgets!
>
> if you want your 5 widgets to have same background as JPanel,
> then set their background to null (for AWT) or make them not opaque (for 
> swing).
> Otherwise in setBackground check if your widgets are not null.

    What if he wants the background of his subclass to be transparent, but 
the 5 widgets to be opaque?

E.g., he has a widget shaped like:

 ----       ----
|    |     |    |
|    |-----|    |
|               |
 ----|     |----
     |     |
 ----|     |----
|               |
|    |-----|    |
|    |     |    |
 ----       ----


    - Oliver 

0
Reply Oliver 6/5/2006 9:42:31 PM

>  What if he wants the background of his subclass to be transparent, but 
> the 5 widgets to be opaque?
>
> E.g., he has a widget shaped like:
>

as I said, he should just check in setBackground() if his widgets was alredy 
created.
If they are null, the he knows, ok it was call in constructor of superclass,
in this case he can a) just ignore or b) call super.setBackground()

Andrey

-- 
http://uio.imagero.com Unified I/O for Java
http://reader.imagero.com Java image reader
http://jgui.imagero.com Java GUI components and utilities


0
Reply Andrey 6/5/2006 10:01:59 PM

Fred Kleinschmidt wrote:
> In my opinion, JLabel should not call setText() in its constructor.
> If it really wants to do that, it should do something like:
>    ((JLabel)this).setText(...);
> so that it calls its own setText(), not my overriding method.

Wouldn't that call the overriding method anyway?
0
Reply Richard 6/6/2006 12:05:00 AM

"Oliver Wong" <owong@castortech.com> wrote in message 
news:bz1hg.32139$JX1.2993@edtnps82...
>
> "Andrey Kuznetsov" <spam0@imagero.com.invalid> wrote in message 
> news:e62504$k91$1@online.de...
>>> One place that this causes problems is in a composite component.
>>> Suppose my subclass of JPanel creates 5 other widgets.
>>> Now suppose that I want to reset the background of all of these
>>> components when the user invokes my setBackground() method.
>>> So in my setBackground() i do this:
>>>   w1.setBackground(color);
>>>   w2.setBackground(color);
>>> etc.
>>>
>>> But my setBackground() method gets called by JPanel's
>>> constructor BEFORE my constructor has created any of these widgets!
>>
>> if you want your 5 widgets to have same background as JPanel,
>> then set their background to null (for AWT) or make them not opaque (for 
>> swing).
>> Otherwise in setBackground check if your widgets are not null.
>
>    What if he wants the background of his subclass to be transparent, but 
> the 5 widgets to be opaque?
>
> E.g., he has a widget shaped like:
>
> ----       ----
> |    |     |    |
> |    |-----|    |
> |               |
> ----|     |----
>     |     |
> ----|     |----
> |               |
> |    |-----|    |
> |    |     |    |
> ----       ----
>
>
>    - Oliver

Yes, I know how to solve the background problem using
these (and other) solutions. However, Andrey has missed
the point. The point is that I do not think any of MY
non-static public methods should be invoked before
MY constructor has been completely executed.

My setValues() method (or setBackground) should not
have to keep checking to see if some variable is null,
and ignore the request if it is. What happens if null is
a legitimate value? So the app sets it to null, and then
it can never be reset again, since I ignore the call
if it is null!

For example, here's one that took me quite a while to
figure out what was wrong. I subclasses JLabel to create
a label that has multi-line capability when it is passed
a String with embedded newlines (not HTML). It
just prepends "<HTML>", replaces all newlines
with "<BR>", and appends "</HTML>".

A snippet of the original:

public class JMultiLineLabel extends JLabel
{
   private String requested_text = null;
   private String converted_text = null;

  public JMultiLineLabel(String text, Icon icon, int horizontalAlignment) {
      super( text, icon, horizontalAlignment );
  }

  public void setText( String text ) {
       requested_text = text;
      converted_text = checkText(text);
      super.setText( converted_text );
   }

   private void checkText( String text ) {
      /* Code to convert 'text' to HTML */
    ...
  }
}

The above does not work - the text is always null.
The reason is in the order in which calls are made:
1) in my constructor, super() is called.
2) super() invokes setText(), which executes my setText().
3) my setText() converts the input text into HTML
    and stores it in converted_text.
4) The rest of my constructor is then invoked. It looks like
    there no other statements there, but in reality, this is when
   the instance variables are initialized - AFTER super()
   is called, not before. So requested_text and
   converted_text are set to null.

Well, I COULD call setText() in MY constructor, but that
does the same work again to convert to HTML that was
already done once, then discarded. Why do it twice?

The solution was to declare the private variables
without explicitly setting them to null:
   private String converted_text;

However, if I wanted to do something with converted_text
in the body of my constructor, the compiler then complains that
I am using a uninitialized variable.

-- 
Fred L. Kleinschmidt
Boeing Associate Technical Fellow
Technical Architect, Software Reuse Project



0
Reply Fred 6/6/2006 2:47:56 PM

Fred Kleinschmidt wrote:
> Is it just me, or does Swing do things in a bad way?
> 
> For example, if I subclass a Swing class, that superclass's
> constructor will be executed as the implicit first statement
> in my constructor. This is as all Java constructors work.
> 
> However, many Swing constructors will call setFont(),
> setForeground(), and setBackground().
> 
> JLabel's constructor also calls setText().
> 
> Now this is all done BEFORE any of my constructor's
> statements are executed. However, due to  polymorphism,
> if I override any of the above-named methods, it is
> MY method that gets executed.

If you read the Effective Java book by Joshua Bloch, he does say
calling non-final methods from constructors is bad style, but
it's probably too late to change it in Swing without making incompatible 
changes to the API. You could try using an "initialized" flag in your 
subclass, but I'm not sure even that would be reliable.

-- 
Judy Szikora, Apprisant Technologies Inc.
http://www.apprisant.com
0
Reply Judy 6/6/2006 3:11:01 PM

>  public void setText( String text ) {
>       requested_text = text;
>      converted_text = checkText(text);
>      super.setText( converted_text );
>   }
>
>   private void checkText( String text ) {
>      /* Code to convert 'text' to HTML */
>    ...
>  }

I just wondering that you use void method to assign String variable.

Andrey

-- 
http://uio.imagero.com Unified I/O for Java
http://reader.imagero.com Java image reader
http://jgui.imagero.com Java GUI components and utilities


0
Reply Andrey 6/6/2006 4:03:59 PM

> For example, here's one that took me quite a while to
> figure out what was wrong. I subclasses JLabel to create
> a label that has multi-line capability when it is passed
> a String with embedded newlines (not HTML). It
> just prepends "<HTML>", replaces all newlines
> with "<BR>", and appends "</HTML>".

I was a bit curios and implemented your JMultilineLabel.
And it works as expected.

here is quick and dirty implementation (not error free):

public class JMultiLineLabel extends JLabel {
    private String requested_text; //assigned but never accessed
    private String converted_text;

    public JMultiLineLabel(String text, Icon icon, int horizontalAlignment) 
{
        super(text, icon, horizontalAlignment);
    }

    public void setText(String text) {
        requested_text = text;
        converted_text = checkText(text);
        super.setText(converted_text);
    }

    private String checkText(String text) {
        StringBuffer sb = new StringBuffer();
        sb.append("<html>");
        int count = text.length();
        for (int i = 0; i < count; i++) {
            char c = text.charAt(i);
            if(c == 10 || c == 13) {
                sb.append("<br>");
                char c0 = text.charAt(i + 1);
                if(c0 == 10 || c0 == 13) {
                    i++;
                }
            }
            else {
                sb.append(c);
            }
        }
        sb.append("</html>");
        return sb.toString();
    }

    public static void main(String[] args) {
        JMultiLineLabel label = new JMultiLineLabel("abc\ndef\ngeh", null, 
JLabel.LEFT);
        System.out.println(label.getText());
    }
}

the output was "<html>abc<br>def<br>geh</html>"

So I assume you have some other errors in your class.

Andrey

-- 
http://uio.imagero.com Unified I/O for Java
http://reader.imagero.com Java image reader
http://jgui.imagero.com Java GUI components and utilities


0
Reply Andrey 6/6/2006 4:23:55 PM

"Fred Kleinschmidt" <fred.l.kleinmschmidt@boeing.com> writes:

> Yes, I know how to solve the background problem using
> these (and other) solutions. However, Andrey has missed
> the point. The point is that I do not think any of MY
> non-static public methods should be invoked before
> MY constructor has been completely executed.

Well, I don't think that is at all realistic, given the object model
that Java is based on.  Since there isn't any call to a standardized
initialization routine, other than the constructor mechanism, there
really isn't any other place for calls to methods that setup the widget
in question.

An alternative Swing implementation where you would have to, for example
write 

      new Label().initialize();

would be possible, but it is a little bit more unwieldy.  It would,
however, give one the flexibilty to schedule via the super() call,
exactly when the super class initializer is called.  It would also
pretty much require any INITIALIZE method to explicitly call the
supermethod or risk having things break.

So, it would be possible to gain flexibility at the cost of a more
complicated creation protocol and the likely introduction of bugs as
programmers forget to invoke the super class' initialize method.

> My setValues() method (or setBackground) should not
> have to keep checking to see if some variable is null,
> and ignore the request if it is. What happens if null is
> a legitimate value? So the app sets it to null, and then
> it can never be reset again, since I ignore the call
> if it is null!

That is one of the evils of in-band signalling.  I would think that the
proper solution would be to have a boolean instance variable
"isInitialized" that is is initially false and that you set to true at
the end of your constructor.  Then you don't have to worry about
legitimate values for any other components.  All of your overriding
method bodies would need to have something like

   if (isInitialized) {
     ... my special code ...
   } else {
     super();
   }

This takes advantage of the default initial value of instance variables,
which happens before any superclass constructors are called. Note that
this is different from the explicit assignment via instance
initializers, which (as you point out in the example that I snipped
below) occurs AFTER constructors are run.

Consider the following example:

class TestSuper {
  TestSuper () {
    System.out.println("Super Constructor");
    meth();
  }

  void meth () {
    System.out.println("Super method");
  }
}

public class Test extends TestSuper {
  
  boolean initialized;

  public Test () {
    System.out.println("Constructor");
    initialized = true;
  }

  void meth () {
      System.out.println("Base method, initialized = " + initialized);
  }

  public static void main (String[] args) {
    Test t = new Test();
    System.out.println("Instance Created");
    t.meth();
  }
}


which executes and prints the following:

Super Constructor
Base method, initialized = false
Constructor
Instance Created
Base method, initialized = true


> For example, here's one that took me quite a while to
> figure out what was wrong. ....

I agree that working out the details of exactly what gets done during
instance creation, and in what order is tricky.  I had to go back and
carefully read the Java Specification to work out exactly when different
parts of the initialization occurred, and what the guarantees are about
initial values.  In the process, I was mildly surprised that the
instance variable initializers were run AFTER superclass constructors.
At least the default initial values were available BEFORE then.

-- 
Thomas A. Russ,  USC/Information Sciences Institute
0
Reply tar 6/6/2006 5:50:27 PM

"Judy Szikora" <nospam@apprisant.com> wrote in message 
news:9Wghg.248498$WI1.221737@pd7tw2no...
> Fred Kleinschmidt wrote:
>> Is it just me, or does Swing do things in a bad way?
>>
>> For example, if I subclass a Swing class, that superclass's
>> constructor will be executed as the implicit first statement
>> in my constructor. This is as all Java constructors work.
>>
>> However, many Swing constructors will call setFont(),
>> setForeground(), and setBackground().
>>
>> JLabel's constructor also calls setText().
>>
>> Now this is all done BEFORE any of my constructor's
>> statements are executed. However, due to  polymorphism,
>> if I override any of the above-named methods, it is
>> MY method that gets executed.
>
> If you read the Effective Java book by Joshua Bloch, he does say
> calling non-final methods from constructors is bad style, but
> it's probably too late to change it in Swing without making incompatible 
> changes to the API. You could try using an "initialized" flag in your 
> subclass, but I'm not sure even that would be reliable.
>
Actually it is pretty easy to "fix".

Just add a private doSetText(...) method. Then the constructor calls
doSetText(...), and setText calls doSetText().

> -- 
> Judy Szikora, Apprisant Technologies Inc.
> http://www.apprisant.com 


0
Reply Fred 6/6/2006 7:37:55 PM

"Andrey Kuznetsov" <spam0@imagero.com.invalid> wrote in message 
news:e64a6f$586$1@online.de...
>> For example, here's one that took me quite a while to
>> figure out what was wrong. I subclasses JLabel to create
>> a label that has multi-line capability when it is passed
>> a String with embedded newlines (not HTML). It
>> just prepends "<HTML>", replaces all newlines
>> with "<BR>", and appends "</HTML>".
>
> I was a bit curios and implemented your JMultilineLabel.
> And it works as expected.
>
> here is quick and dirty implementation (not error free):
>
> public class JMultiLineLabel extends JLabel {
>    private String requested_text; //assigned but never accessed
>    private String converted_text;

This code is essentially what I had. The main difference
(i.e., the thing that caused the problem) was that I had
     private String requested_text = null;
     private String converted_text = null;

Note the explicit setting to null.
(I also have a getRequestedText() method, which returns requested_text)

>
>    public JMultiLineLabel(String text, Icon icon, int horizontalAlignment) 
> {
>        super(text, icon, horizontalAlignment);
>    }
>
>    public void setText(String text) {
>        requested_text = text;
>        converted_text = checkText(text);
>        super.setText(converted_text);
>    }
>
>    private String checkText(String text) {
>        StringBuffer sb = new StringBuffer();
>        sb.append("<html>");
>        int count = text.length();
>        for (int i = 0; i < count; i++) {
>            char c = text.charAt(i);
>            if(c == 10 || c == 13) {
>                sb.append("<br>");
>                char c0 = text.charAt(i + 1);
>                if(c0 == 10 || c0 == 13) {
>                    i++;
>                }
>            }
>            else {
>                sb.append(c);
>            }
>        }
>        sb.append("</html>");
>        return sb.toString();
>    }
>
>    public static void main(String[] args) {
>        JMultiLineLabel label = new JMultiLineLabel("abc\ndef\ngeh", null, 
> JLabel.LEFT);
>        System.out.println(label.getText());
>    }
> }
>
> the output was "<html>abc<br>def<br>geh</html>"
>
> So I assume you have some other errors in your class.
>
> Andrey
>
> -- 
> http://uio.imagero.com Unified I/O for Java
> http://reader.imagero.com Java image reader
> http://jgui.imagero.com Java GUI components and utilities
>
>
-- 
Fred L. Kleinschmidt
Boeing Associate Technical Fellow
Technical Architect, Software Reuse Project


0
Reply Fred 6/6/2006 7:41:56 PM

> This code is essentially what I had. The main difference
> (i.e., the thing that caused the problem) was that I had
>     private String requested_text = null;
>     private String converted_text = null;

this makes no sense - it is anyway initialized to null, so it is better to 
avoid such inits.

> Note the explicit setting to null.
> (I also have a getRequestedText() method, which returns requested_text)

this does not matters,
instance variables should be initialized before constructor call,
since constructor may rely on their values.

Just to be 200% sure I made explicit initialization and got same result - it 
works.

Andrey

-- 
http://uio.imagero.com Unified I/O for Java
http://reader.imagero.com Java image reader
http://jgui.imagero.com Java GUI components and utilities


0
Reply Andrey 6/6/2006 8:21:07 PM

"Andrey Kuznetsov" <spam0@imagero.com.invalid> wrote in message 
news:e64o36$1ms$1@online.de...
>> This code is essentially what I had. The main difference
>> (i.e., the thing that caused the problem) was that I had
>>     private String requested_text = null;
>>     private String converted_text = null;
>
> this makes no sense - it is anyway initialized to null, so it is better to 
> avoid such inits.
>
>> Note the explicit setting to null.
>> (I also have a getRequestedText() method, which returns requested_text)
>
> this does not matters,
> instance variables should be initialized before constructor call,
> since constructor may rely on their values.
>
> Just to be 200% sure I made explicit initialization and got same result - 
> it works.

Try adding this methoid, along with explicit initialization:

       public String getText() {
           return converted_text;
       }


>
> Andrey
>
-- 
Fred L. Kleinschmidt
Boeing Associate Technical Fellow
Technical Architect, Software Reuse Project


0
Reply Fred 6/6/2006 10:30:47 PM

Fred,

> Try adding this methoid, along with explicit initialization:
>
>       public String getText() {
>           return converted_text;
>       }

yes explicit init comes *between* super constructor and your constructor.
Without explicit init Object variable is created with null value;
This tell us that member variables are created before super constructor 
called.
But why do you need explicit init?

Furthermore you may just call another super constructor and then set text 
after calling super():

    public JMultiLineLabel(String text, Icon icon, int horizontalAlignment) 
{
        super(icon, horizontalAlignment);
        setText(text);
    }

this form will work *anyway*.

Andrey

-- 
http://uio.imagero.com Unified I/O for Java
http://reader.imagero.com Java image reader
http://jgui.imagero.com Java GUI components and utilities


0
Reply Andrey 6/7/2006 12:14:43 AM

"Andrey Kuznetsov" <spam0@imagero.com.invalid> wrote in message 
news:e655p6$mmn$1@online.de...
> Fred,
>
>> Try adding this methoid, along with explicit initialization:
>>
>>       public String getText() {
>>           return converted_text;
>>       }
>
> yes explicit init comes *between* super constructor and your constructor.
> Without explicit init Object variable is created with null value;
> This tell us that member variables are created before super constructor 
> called.
> But why do you need explicit init?
>
> Furthermore you may just call another super constructor and then set text 
> after calling super():
>
>    public JMultiLineLabel(String text, Icon icon, int horizontalAlignment) 
> {
>        super(icon, horizontalAlignment);
>        setText(text);
>    }
>
> this form will work *anyway*.

True, but setText has already been executed once by this time, since
super() invokes it, so this duplicates the effort.

I don't need the explicit init (actually, in this case, I not only
don't need it, using it is dead wrong). It's just that I happened to
do it in this case, and it took me a long time trying to figure out
what was wrong.

The real problem would be: what if setText() referred to
some instance variable? For example, maybe my constructor
contains some flag specifying whether to use <BR> or <P>
as the newline substitute. But I can't set that flag until
after the super constructor is called. So when the super
constructor calls setText(), it uses the default value of the
flag  instead of the value that will later be set in my
constructor. Then I would be forced to explicitly call
setText() in my constructor, executing it again using the
correct value of the flag.

>
> Andrey
>
> -- 
> http://uio.imagero.com Unified I/O for Java
> http://reader.imagero.com Java image reader
> http://jgui.imagero.com Java GUI components and utilities
>
>
-- 
Fred L. Kleinschmidt
Boeing Associate Technical Fellow
Technical Architect, Software Reuse Project


0
Reply Fred 6/7/2006 2:57:10 PM

> True, but setText has already been executed once by this time, since
> super() invokes it, so this duplicates the effort.

> I don't need the explicit init (actually, in this case, I not only
> don't need it, using it is dead wrong). It's just that I happened to
> do it in this case, and it took me a long time trying to figure out
> what was wrong.
>
> The real problem would be: what if setText() referred to
> some instance variable? For example, maybe my constructor
> contains some flag specifying whether to use <BR> or <P>
> as the newline substitute. But I can't set that flag until
> after the super constructor is called. So when the super
> constructor calls setText(), it uses the default value of the
> flag  instead of the value that will later be set in my
> constructor. Then I would be forced to explicitly call
> setText() in my constructor, executing it again using the
> correct value of the flag.

since you pass null to super constructor,
this shouldn't be a problem and overhead for second call is minimal.

Andrey

-- 
http://uio.imagero.com Unified I/O for Java
http://reader.imagero.com Java image reader
http://jgui.imagero.com Java GUI components and utilities


0
Reply Andrey 6/7/2006 3:59:38 PM

"Andrey Kuznetsov" <spam0@imagero.com.invalid> writes:

> > This code is essentially what I had. The main difference
> > (i.e., the thing that caused the problem) was that I had
> >     private String requested_text = null;
> >     private String converted_text = null;
> 
> this makes no sense - it is anyway initialized to null, so it is better to 
> avoid such inits.
> 
> > Note the explicit setting to null.
> > (I also have a getRequestedText() method, which returns requested_text)
> 
> this does not matters,
> instance variables should be initialized before constructor call,
> since constructor may rely on their values.

Well, instance variables can't be initialized BEFORE the constructor
call, because it is only the call to the constructor that tells Java
that a new instance is needed.  The question then becomes one of exactly
WHEN in the sequence of automatic actions the variables are initialized.

As it turns out, Java does things in the following order:
  Set all instance variables to DEFAULT for type (not initial value!)
  Invoke superclass constructor
  Set this class' instance variables to initial values if given
  Run the body of this class'constructor.

So, the issue was that values were being set for instance variables in
the superclass constructor and then clobbered when the instance
variables were set to their initial values.  It would have been nice if
all of the instance variables were initialized first, but I guess this
would have required two traversals of the class hierarchy (for instance
initialization and then constructor execution) rather than just the one
traversal that is actually implemented.

-- 
Thomas A. Russ,  USC/Information Sciences Institute
0
Reply tar 6/7/2006 4:34:41 PM

"Fred Kleinschmidt" <fred.l.kleinmschmidt@boeing.com> writes:

>  > Fred Kleinschmidt wrote:
> >> Is it just me, or does Swing do things in a bad way?
> >>
> >> For example, if I subclass a Swing class, that superclass's
> >> constructor will be executed as the implicit first statement
> >> in my constructor. This is as all Java constructors work.
> >>
> >> However, many Swing constructors will call setFont(),
> >> setForeground(), and setBackground().
> >>
> >> JLabel's constructor also calls setText().
> >>
> >> Now this is all done BEFORE any of my constructor's
> >> statements are executed. However, due to  polymorphism,
> >> if I override any of the above-named methods, it is
> >> MY method that gets executed.
> 
> Actually it is pretty easy to "fix".
> 
> Just add a private doSetText(...) method. Then the constructor calls
> doSetText(...), and setText calls doSetText().

That won't work for the original poster's problem.  His problem is that
the constructor in the SUPERCLASS was calling setText.  He can't change
the implementation of the Swing JLabel's constructor.

So he has to do something different.

-- 
Thomas A. Russ,  USC/Information Sciences Institute
















0
Reply tar 6/7/2006 4:37:14 PM

"Thomas A. Russ" <tar@sevak.isi.edu> wrote in message 
news:ymiy7w8divy.fsf@sevak.isi.edu...
> "Andrey Kuznetsov" <spam0@imagero.com.invalid> writes:
>
>> > This code is essentially what I had. The main difference
>> > (i.e., the thing that caused the problem) was that I had
>> >     private String requested_text = null;
>> >     private String converted_text = null;
>>
>> this makes no sense - it is anyway initialized to null, so it is better 
>> to
>> avoid such inits.
>>
>> > Note the explicit setting to null.
>> > (I also have a getRequestedText() method, which returns requested_text)
>>
>> this does not matters,
>> instance variables should be initialized before constructor call,
>> since constructor may rely on their values.
>
> Well, instance variables can't be initialized BEFORE the constructor
> call, because it is only the call to the constructor that tells Java
> that a new instance is needed.  The question then becomes one of exactly
> WHEN in the sequence of automatic actions the variables are initialized.
>
> As it turns out, Java does things in the following order:
>  Set all instance variables to DEFAULT for type (not initial value!)
>  Invoke superclass constructor
>  Set this class' instance variables to initial values if given
>  Run the body of this class'constructor.
>
> So, the issue was that values were being set for instance variables in
> the superclass constructor and then clobbered when the instance
> variables were set to their initial values.  It would have been nice if
> all of the instance variables were initialized first, but I guess this
> would have required two traversals of the class hierarchy (for instance
> initialization and then constructor execution) rather than just the one
> traversal that is actually implemented.

And thus it would be nice if constructors were careful NOT to call any 
public
methods that might be overridden by subclasses.
>
> -- 
> Thomas A. Russ,  USC/Information Sciences Institute
-- 
Fred L. Kleinschmidt
Boeing Associate Technical Fellow
Technical Architect, Software Reuse Project


0
Reply Fred 6/7/2006 11:11:42 PM

"Fred Kleinschmidt" <fred.l.kleinmschmidt@boeing.com> writes:

> "Thomas A. Russ" <tar@sevak.isi.edu> wrote in message 
> >
> > So, the issue was that values were being set for instance variables in
> > the superclass constructor and then clobbered when the instance
> > variables were set to their initial values.  It would have been nice if
> > all of the instance variables were initialized first, but I guess this
> > would have required two traversals of the class hierarchy (for instance
> > initialization and then constructor execution) rather than just the one
> > traversal that is actually implemented.
> 
> And thus it would be nice if constructors were careful NOT to call any 
> public
> methods that might be overridden by subclasses.

Well, I go see this going either way.  On the one hand, you would avoid
the particular bug that caused you grief.  On the other hand, it would
mean that you couldn't alter the initialization behavior from the super
class constructor by overriding the method.  I can imagine that could
come in handy in some circumstances as well.  But that might be too
obscure a case to be considered a good design principle.

What would be nice is if any such public methods were mentioned in the
documentation. 

-- 
Thomas A. Russ,  USC/Information Sciences Institute
0
Reply tar 6/9/2006 7:06:43 PM

On Fri, 09 Jun 2006 12:06:43 -0700, Thomas A. Russ wrote:

> Well, I go see this going either way.  On the one hand, you would avoid
> the particular bug that caused you grief.  On the other hand, it would
> mean that you couldn't alter the initialization behavior from the super
> class constructor by overriding the method.  I can imagine that could
> come in handy in some circumstances as well.  But that might be too
> obscure a case to be considered a good design principle.

I find it somewhat surprising that Java allows virtual dispatch before
initialization is complete.  Normally a class's methods are written
assuming certain invariants, established by the constructor and maintained
by all methods, hold; prior to the constructor's invocation there may not
be a guarantee that those invariants hold.  Knowing that Java allows this
makes it possible to design around it so invariants are established before
the constructor is called, but it's very odd from my point of view.

It'd make more sense if, during the constructor, virtual dispatch was
suspended and only methods defined in the class or its parents could be
invoked.  That way, as the constructors chain down from Object(), each one
sees a complete parent class instance, and no code is ever called before
the constructor for the class defining it is called.

Yes, I do come from a C++ background.

-Owen
0
Reply Owen 6/10/2006 6:46:40 AM

> Well, I go see this going either way.  On the one hand, you would avoid
> the particular bug that caused you grief.  On the other hand, it would
> mean that you couldn't alter the initialization behavior from the super
> class constructor by overriding the method.  I can imagine that could
> come in handy in some circumstances as well.  But that might be too
> obscure a case to be considered a good design principle.
>
> What would be nice is if any such public methods were mentioned in the
> documentation.

I imagine that in this particular case (JLabel) it would be nice to have 
such constructor:

public class JLabel extends JComponent {
    public JLabel(String text, Icon icon, int horizontalAlignment) {
        init(text, icon, horizontalAlignment);
    }

    protected void init(String text, Icon icon, int horizontalAlignment) {
        setText(text);
        setIcon(icon);
        setHorizontalAlignment(horizontalAlignment);
        updateUI();
        setAlignmentX(LEFT_ALIGNMENT);
    }
}

//we override here init to nothing and
//call super.init after we have initialized required things

public class JMultiLineLabel extends JLabel {
    public JMultiLineLabel (String text, Icon icon, int horizontalAlignment) 
{
        super(text, icon, horizontalAlignment);
        myInit(text, icon, horizontalAlignment);
    }

    protected void init(String text, Icon icon, int horizontalAlignment) {
        //do nothing here
    }

    protected void myInit(String text, Icon icon, int horizontalAlignment) {
        //set some variables here
        super.init(text, icon, horizontalAlignment);
    }
}

Andrey

-- 
http://uio.imagero.com Unified I/O for Java
http://reader.imagero.com Java image reader
http://jgui.imagero.com Java GUI components and utilities


0
Reply Andrey 6/10/2006 10:01:56 AM

Owen Jacobson wrote:

> I find it somewhat surprising that Java allows virtual dispatch before
> initialization is complete.

I'd say there are swings and roundabouts here.  I find it quite irritating in
C++ when I'm unable to specialise initialisation using normal virtual method
calls.


> It'd make more sense if, during the constructor, virtual dispatch was
> suspended and only methods defined in the class or its parents could be
> invoked.

Do you have a suggestion for how that could be implemented efficiently
(remembering that vtables are not the implementation strategy of choice) ?

    -- chris


0
Reply Chris 6/10/2006 12:05:44 PM

On Sat, 10 Jun 2006 13:05:44 +0100, Chris Uppal wrote:

> Owen Jacobson wrote:
> 
>> I find it somewhat surprising that Java allows virtual dispatch before
>> initialization is complete.
> 
> I'd say there are swings and roundabouts here.  I find it quite irritating in
> C++ when I'm unable to specialise initialisation using normal virtual method
> calls.

Yeah.  I think it's more a matter of what you're used to.  Both approaches
are valid, and as you point out the Java approach may be way easier to
implement in the JVM.

>> It'd make more sense if, during the constructor, virtual dispatch was
>> suspended and only methods defined in the class or its parents could be
>> invoked.
> 
> Do you have a suggestion for how that could be implemented efficiently
> (remembering that vtables are not the implementation strategy of choice) ?

Of course not.  That makes far too much sense.  :)  Out of curiousity, how
*are* methods dispatched by the JVM?  It's obvious that an offset-based
vtable would make no sense; it'd break as soon as a parent class was
recompiled, which demonstrably doesn't happen.  I assume it's similar to
a system where each object has a pointer to its class, which in turn has
an associative table from method signatures to method implementations.

Owen
0
Reply Owen 6/10/2006 5:59:34 PM

Owen Jacobson wrote:
  > Out of curiousity, how
> *are* methods dispatched by the JVM?  It's obvious that an offset-based
> vtable would make no sense; it'd break as soon as a parent class was
> recompiled, which demonstrably doesn't happen.  I assume it's similar to
> a system where each object has a pointer to its class, which in turn has
> an associative table from method signatures to method implementations.

Note that unlike C++, Java can mandate linker behavior, so vtable
offsets (if it uses such; I have no internal knowledge) could be
symbolic until the defining parent class is loaded.  It does seem
to pull in all the classes at load time, saving the complexity of
delaying binding some of the offsets while letting the program use
others.
0
Reply Robert 6/10/2006 7:33:10 PM

Owen Jacobson wrote:

>  Out of curiousity, how
> *are* methods dispatched by the JVM?  It's obvious that an offset-based
> vtable would make no sense; it'd break as soon as a parent class was
> recompiled, which demonstrably doesn't happen.

It's difficult to say how method dispatch works in "the" JVM because it's
specific to a particular JVM /implementation/.  There is a range of
implementation techniques available (and there are quite a lot of published
papers on the subject -- which is understandable since it's both an interesting
area of study, /and/ of critical importance to the overall execution speed).  I
don't really know what is used in the current range of JVMs from Sun; I guess
that it's something like the following.

Consider a method call like

    o.someMethod();

where o is not an interface type.  So we are talking about "normal" virtual
dispatch, not the more complicated mechanism which is needed when the concrete
types of objects refered to by "o" are not necessarily related by inheritance
(which rules out vtables completely).

The options the JVM has are:

If the containing method or block isn't executed often, then don't try to be
clever, just use a vtable lookup.

Otherwise, if the containing method is executed frequently:

Inline the call to aMethod() if possible and worthwhile.

If not then use a very small (size ~= 1) cache at each call site mapping
concrete types to addresses in memory of the corresponding methods.  So each
method call is converted to a test-and-jump when the cached <type, address> is
"correct".  Fall back to a vtable if the test fails.

If the call site is known to be megaphorphic[*] (fairly rare) then don't bother
with a cache, use a vtable always.  ("megamorphic" means that the single
virtual call site resolves to many different concrete methods during
execution.)

If the call site /is/ via an interface type, then vtables don't work, so other
mean have to be found.  Note that the inlike lookup cache will still work for
interface dispatch, but the fallback has to be different.  There is quite an
extensive literature on this subject alone, but I don't think I've ever seen
anything on what Sun's real JVMs /actually/ do in this case (references
welcome!).

Please remember: the above is my /guess/ -- I'm not claiming to be stating
facts here (unfortunately -- I'd like to know more but there's much less info
available on how things are actually done than there is literature on what the
design options could be.)

BTW, all this stuff all happens in the JVM, so it is in a position to create
vtables, or any other lookup structure it wants, at runtime.  The only
restriction is that it must be able to revoke any assumptions it has made if
new classes are loaded which invalidate them. (E.g. it may have to un-inline a
method call).

It might help you (as a C++-er) to know that what javac does is hardly even
similar to what gcc or MSVC does when compiling C++.   The JVM bytecode set is
/not/ like an instruction set definition for a Von-Neumann machine (it's not
similar, even in concept, to the IA32 or SPARC instruction sets).  JVM byte
code is a high-level, object-oriented, garbage-collected, language in its own
right, and javac's job is the /translate/ Java source into equivalent (and
normally pretty similar) programs written in bytecode.  It's only when the JVM
gets hold of that bytecode that something similar to C++ compilation happens.
In particular it's only then that virtual method calls are converted into
machine-level tests, lookups, and jumps.

    -- chris



0
Reply Chris 6/12/2006 10:57:50 AM

On Mon, 12 Jun 2006 11:57:50 +0100, Chris Uppal wrote:

> Owen Jacobson wrote:
> 
>>  Out of curiousity, how
>> *are* methods dispatched by the JVM?  It's obvious that an offset-based
>> vtable would make no sense; it'd break as soon as a parent class was
>> recompiled, which demonstrably doesn't happen.
> 
> It's difficult to say how method dispatch works in "the" JVM because
> it's specific to a particular JVM /implementation/.

Which I knew.  Serves me right for posting from bed: I get imprecise, even
when talking of precise things.

> I guess that it's something like the following.
> 
> Consider a method call like
> 
>     o.someMethod();
> 
> where o is not an interface type.
[...]
> Otherwise, if the containing method is executed frequently:
> 
> Inline the call to aMethod() if possible and worthwhile.
> 
> If not then use a very small (size ~= 1) cache at each call site mapping
> concrete types to addresses in memory of the corresponding methods.  So
> each method call is converted to a test-and-jump when the cached <type,
> address> is "correct".  Fall back to a vtable if the test fails.

That's... actually, alarmingly elegant.  I've done something a lot like
that for a math library which occasionally re-performed the same function
on the same input arguments, by bolting on a one-element cache.

> If the call site is known to be megaphorphic[*] (fairly rare) then don't
> bother with a cache, use a vtable always.  ("megamorphic" means that the
> single virtual call site resolves to many different concrete methods
> during execution.)

This is for a non-interface invocation site, or I'd disagree with
megamorphism being "rare". :)

> It might help you (as a C++-er) to know that what javac does is hardly
> even similar to what gcc or MSVC does when compiling C++.   The JVM
> bytecode set is /not/ like an instruction set definition for a
> Von-Neumann machine (it's not similar, even in concept, to the IA32 or
> SPARC instruction sets).

Yeah.  I've had a lot of fun with javap and the BCEL tools.  The Sun JVM
does some funny things if you feed it debatably-valid class files
("interfaces" that contain concrete methods, etc).  Or it used to; I
haven't done much with the 1.5 series except write actual code.  I'm
actually much more at home with Java than with C++, but I have a firmer
grasp of how C++ compilers handle some details -- like invocations -- than
of how JVMs deal with it.

-O

0
Reply Owen 6/14/2006 6:16:59 AM

Owen Jacobson wrote:

> > If the call site is known to be megaphorphic[*] (fairly rare) then don't
> > bother with a cache, use a vtable always.  ("megamorphic" means that the
> > single virtual call site resolves to many different concrete methods
> > during execution.)
>
> This is for a non-interface invocation site, or I'd disagree with
> megamorphism being "rare". :)

I don't have figures to hand (I do think I've seen some, but I can't remember
for sure, and anyway I've forgotten where...), but I think they are rarer than
they might appear.  Even a call to a massively overridden method like
toString() is quite likely to be reached by only a tiny subset of all the
classes which implement toString(), unless the call /site/ is in general
purpose code (e.g. String.valueOf(Object)).

    -- chris




0
Reply Chris 6/14/2006 8:48:06 AM

Owen Jacobson wrote:
> On Mon, 12 Jun 2006 11:57:50 +0100, Chris Uppal wrote:
> 
>> If not then use a very small (size ~= 1) cache at each call site mapping
>> concrete types to addresses in memory of the corresponding methods.  So
>> each method call is converted to a test-and-jump when the cached <type,
>> address> is "correct".  Fall back to a vtable if the test fails.
> 
> That's... actually, alarmingly elegant.  I've done something a lot like
> that for a math library which occasionally re-performed the same function
> on the same input arguments, by bolting on a one-element cache.

I can understand the use of this for caching results from complicated 
methods, eg. Don't lookup the db record with the same key again, (if key 
== last_cache return last_result, but as a substitute for a vtable call? 
The jit compiler would surely compile this to something akin to (IA32):

MOV EBX,Object(EAX).VTABLE
CALL [EBX + method_offset]

around 2 cycles plus maybe some branch prediction and pipeline delays 
depending on where the first instruction appears. I would think that the 
vtable lookup would nearly always be faster than a test and conditional 
branch.

Regards,
Richard
0
Reply Richard 6/15/2006 10:59:01 AM

Richard Wilson wrote:

> MOV EBX,Object(EAX).VTABLE
> CALL [EBX + method_offset]
>
> around 2 cycles plus maybe some branch prediction and pipeline delays
> depending on where the first instruction appears. I would think that the
> vtable lookup would nearly always be faster than a test and conditional
> branch.

Don't forget that the conditional branch would be well-predicted (else there's
no point in creating the cache in the first place) so there should be little or
no branch misprediction penalty.  Also branching though an indirection (as in a
vtable) is /not/ an efficient operation.  This has been rather heavily
studied -- I don't have references off-hand but search for "polymorphin inline
cache" to find the (SELF related) papers which introduced the idea, you should
be able to find more (if you are interested) going on from there.

    -- chris




0
Reply Chris 6/15/2006 1:12:15 PM

Owen Jacobson wrote:
> On Fri, 09 Jun 2006 12:06:43 -0700, Thomas A. Russ wrote:
>
> > Well, I go see this going either way.  On the one hand, you would avoid
> > the particular bug that caused you grief.  On the other hand, it would
> > mean that you couldn't alter the initialization behavior from the super
> > class constructor by overriding the method.  I can imagine that could
> > come in handy in some circumstances as well.  But that might be too
> > obscure a case to be considered a good design principle.
>
> I find it somewhat surprising that Java allows virtual dispatch before
> initialization is complete.  Normally a class's methods are written
> assuming certain invariants, established by the constructor and maintained
> by all methods, hold; prior to the constructor's invocation there may not
> be a guarantee that those invariants hold.  Knowing that Java allows this
> makes it possible to design around it so invariants are established before
> the constructor is called, but it's very odd from my point of view.
>
> It'd make more sense if, during the constructor, virtual dispatch was
> suspended and only methods defined in the class or its parents could be
> invoked.  That way, as the constructors chain down from Object(), each one
> sees a complete parent class instance, and no code is ever called before
> the constructor for the class defining it is called.
>
> Yes, I do come from a C++ background.

And back to the original topic of the thread, I suggest that if you're
really worried, call mainly methods that are either private or final in
your constructors. At least, document (in doc comments, preferably) for
each public or protected non-final method called by a constructor that
it may be invoked on an object that's still under construction, so
developers writing overrides know.

Actually, one thing javadoc could use is a uniform and easy way to
separate the main body of a method or class comment into two sections,
each optional, one oriented at documenting the method for callers and
one oriented at documenting the method for implementors. The former, or
both, would be present on interfaces and their methods, for example,
and the latter on AbstractFoo or FooAdapter type classes that provide
skeleton implementations; you'd only need the former on final methods
and constructors. (You can probably get away with putting some <H4> or
weaker headings in manually, but ick. New doc tags, say "@caller" and
"@impl", would be cleaner for inserting suitable headings
automagically.)

Of course I could write a whole book of Java wishlist items. Ability to
require certain constructor signatures or static methods in an
interface; a less clunky JNI; some assurance that wrapping a class
won't double the time it takes to invoke its methods (and wrapping is
too damn useful to allow it to be inefficient; to bolt an existing
class into a new framework by adapting it to implement a particular
interface, for example)...you can't always just subclass it and say the
subclass implements foo; you generally need to write some new methods
that call differently-named or -signatured superclass methods, or
actually wrap the thing rather than subclass it, and ideally a method
that does little but call another method should not be any worse for
efficiency than a dynamically-dispatched call...

0
Reply Twisted 6/15/2006 7:53:27 PM

"Twisted" <twisted0n3@gmail.com> wrote in message 
news:1150401206.989153.234920@p79g2000cwp.googlegroups.com...
>
> And back to the original topic of the thread, I suggest that if you're
> really worried, call mainly methods that are either private or final in
> your constructors. At least, document (in doc comments, preferably) for
> each public or protected non-final method called by a constructor that
> it may be invoked on an object that's still under construction, so
> developers writing overrides know.

    You could probably at best only say in the comments "*MAYBE* this method 
will get called by the constructor". You don't want to guarantee that you'll 
be calling these methods in the constructor as part of your public contract. 
You might, after all, change the implementation of your constructor someday!

    - Oliver 

0
Reply Oliver 6/15/2006 9:45:04 PM

32 Replies
299 Views

(page loaded in 0.318 seconds)

Similiar Articles:












7/25/2012 12:37:47 PM


Reply: