An enum mystery solved

Discussion in 'Java' started by Roedy Green, Aug 26, 2005.

  1. Roedy Green

    Roedy Green Guest

    I was baffled why the compiler would not let enum constructors access
    the enum's static variables.

    I consider it a bug, but an understandable bug.

    I think the reason is the enum invokes the constructors for the enum
    constants in static init code. Somebody was worried that the static
    initialisation would not be complete. Yet it is quite safe since the
    enum constants are the last bit of static init.

    --
    Canadian Mind Products, Roedy Green.
    http://mindprod.com Again taking new Java programming contracts.
    Roedy Green, Aug 26, 2005
    #1
    1. Advertising

  2. Roedy Green

    Roedy Green Guest

    On Fri, 26 Aug 2005 05:58:23 GMT, Roedy Green
    <> wrote or quoted :

    >I think the reason is the enum invokes the constructors for the enum
    >constants in static init code. Somebody was worried that the static
    >initialisation would not be complete. Yet it is quite safe since the
    >enum constants are the last bit of static init.


    I have placed an RFE with Sun to have the restriction repealed. In the
    meantime, oddly you can call static METHODS that access the static
    variables.

    --
    Canadian Mind Products, Roedy Green.
    http://mindprod.com Again taking new Java programming contracts.
    Roedy Green, Aug 26, 2005
    #2
    1. Advertising

  3. Roedy Green

    Hemal Pandya Guest


    > I was baffled why the compiler would not let enum constructors access
    > the enum's static variables.


    If I understand the issue correctly, jls3 8.9 addresses this issue: It
    is compile-time error to reference a non-constant (ยง15.28) static
    field of an enum type from its constructors, instance initializer
    blocks, or instance variable initializer expressions.

    >
    > I consider it a bug, but an understandable bug.
    >
    > I think the reason is the enum invokes the constructors for the enum
    > constants in static init code. Somebody was worried that the static
    > initialisation would not be complete. Yet it is quite safe since the
    > enum constants are the last bit of static init.
    >

    A small test seems to indicate that enum constants are the /first/ bit
    of static init and that the static final variables are not initialized
    during this enum constant construction.
    Hemal Pandya, Aug 26, 2005
    #3
  4. Hemal Pandya wrote:
    >
    > A small test seems to indicate that enum constants are the /first/ bit
    > of static init and that the static final variables are not initialized
    > during this enum constant construction.


    I think what Roedy missed was the distinction between a compile time
    constant and other static finals. A compile time constant will be
    inlined and therefore there is no problem. A static final initialised
    within static { }/<clinit> will cause a problem.

    enum Const {
    X;
    private static final int x = 1; // compile time constant
    private int i;
    Const() {
    i = x; // fine
    }
    }

    enum Final {
    X;
    private static final int x = false?null:1; // null is not constant
    private int i;
    Final() {
    i = x; // !! illegal reference to static field from initializer
    }
    }

    You can have a static final reference the enumerated.

    enum Ref {
    X;
    private static final Ref x = X; // fine
    }

    So constructors must run before user static initialisation. You should
    be able to access the static of other classes however.

    Tom Hawtin
    --
    Unemployed English Java programmer
    http://jroller.com/page/tackline/
    Thomas Hawtin, Aug 26, 2005
    #4
  5. Roedy Green

    Hemal Pandya Guest

    > In the
    > meantime, oddly you can call static METHODS that access the static
    > variables.


    This seems analogous to the "illegal forward reference" warning when
    one static final variable is refers to another static final (but not
    constant) variable but can get NullPointerException if the same forward
    reference is indirectly achieved using a static method.
    Hemal Pandya, Aug 26, 2005
    #5
  6. Roedy Green

    Roedy Green Guest

    On 26 Aug 2005 03:27:04 -0700, "Hemal Pandya" <>
    wrote or quoted :

    >A small test seems to indicate that enum constants are the /first/ bit
    >of static init and that the static final variables are not initialized
    >during this enum constant construction.


    There is something deeply wrong when Java starts adding arbitrary
    restrictions like that making no sense in the high level language.

    I decompiled and discovered the construction of the enum constants
    came last. What is your code that the inits come after the
    construction? Why could not the compiler put it before and be done
    with this silly restriction, simultaneously making it safer to use
    static methods.

    here is an example:

    public enum Trees
    {
    PINE( true ),
    ASPEN ( false );

    private static int coniferousCount;

    Trees( boolean coniferous )
    {
    counter( coniferous );
    }

    static void counter( boolean coniferous )
    {
    if ( coniferous ) coniferousCount++;
    }

    }


    decompiles as:

    public final class Trees extends Enum
    {

    public static final Trees[] values()
    {
    return (Trees[])$VALUES.clone();
    }

    public static Trees valueOf(String s)
    {
    return (Trees)Enum.valueOf(Trees, s);
    }

    private Trees(String s, int i, boolean flag)
    {
    super(s, i);
    counter(flag);
    }

    static void counter(boolean flag)
    {
    if(flag)
    coniferousCount++;
    }

    public static final Trees PINE;
    public static final Trees ASPEN;
    private static int coniferousCount;
    private static final Trees $VALUES[];

    static
    {
    PINE = new Trees("PINE", 0, true);
    ASPEN = new Trees("ASPEN", 1, false);
    $VALUES = (new Trees[] {
    PINE, ASPEN
    });
    }
    }
    --
    Canadian Mind Products, Roedy Green.
    http://mindprod.com Again taking new Java programming contracts.
    Roedy Green, Aug 26, 2005
    #6
  7. Roedy Green

    Roedy Green Guest


    >enum Final {
    > X;
    > private static final int x = false?null:1; // null is not constant
    > private int i;
    > Final() {
    > i = x; // !! illegal reference to static field from initializer
    > }
    >}
    >

    Here is your code modified slightly to make it compile:

    enum Final {
    X; // enum constant
    private static final int x = Math.min(2,1);
    // final but not constant
    private int i;
    Final()
    {
    // i = x; // !! illegal reference to static field from
    initializer
    }
    }

    Here is how it decompiles:

    final class Final extends Enum
    {

    public static final Final[] values()
    {
    return (Final[])$VALUES.clone();
    }

    public static Final valueOf(String s)
    {
    return (Final)Enum.valueOf(Final, s);
    }

    private Final(String s, int j)
    {
    super(s, j);
    }

    public static final Final X;
    private static final int x = Math.min(2, 1);
    private int i;
    private static final Final $VALUES[];

    static
    {
    X = new Final("X", 0);
    $VALUES = (new Final[] {
    X
    });
    }
    }

    You can see that had the i=x been allowed, it should have been fine.
    The constructors are run after the x = Math.min(2,1) are they not?
    --
    Canadian Mind Products, Roedy Green.
    http://mindprod.com Again taking new Java programming contracts.
    Roedy Green, Aug 26, 2005
    #7
  8. Roedy Green wrote:
    > On 26 Aug 2005 03:27:04 -0700, "Hemal Pandya" <>
    > wrote or quoted :
    >
    >
    >>A small test seems to indicate that enum constants are the /first/ bit
    >>of static init and that the static final variables are not initialized
    >>during this enum constant construction.

    >
    >
    > There is something deeply wrong when Java starts adding arbitrary
    > restrictions like that making no sense in the high level language.


    Initialisation has got to be done in some order. Doing it in the order
    presented in the source code seems like the method of least surprises to me.

    > I decompiled and discovered the construction of the enum constants
    > came last. What is your code that the inits come after the
    > construction? Why could not the compiler put it before and be done
    > with this silly restriction, simultaneously making it safer to use
    > static methods.
    >
    > here is an example:
    >
    > public enum Trees
    > {
    > PINE( true ),
    > ASPEN ( false );
    >
    > private static int coniferousCount;
    >
    > Trees( boolean coniferous )
    > {
    > counter( coniferous );
    > }
    >
    > static void counter( boolean coniferous )
    > {
    > if ( coniferous ) coniferousCount++;
    > }
    >
    > }


    This is an irrelevant example. It does not do any user static
    initialisation.

    Here's my example:

    enum Order {
    X;
    private static final long time = System.currentTimeMillis();
    }

    $ javap -c Order
    ....
    static {};
    Code:
    0: new #4; //class Order
    3: dup
    4: ldc #7; //String X
    6: iconst_0
    7: invokespecial #8; //Method "<init>":(Ljava/lang/String;I)V
    10: putstatic #9; //Field X:LOrder;
    13: iconst_1
    14: anewarray #4; //class Order
    17: dup
    18: iconst_0
    19: getstatic #9; //Field X:LOrder;
    22: aastore
    23: putstatic #1; //Field $VALUES:[LOrder;
    26: invokestatic #10; //Method
    java/lang/System.currentTimeMillis:()J
    29: putstatic #11; //Field time:J
    32: return


    Bytes [0, 13) creates the constant. Bytes [13, 26) do the rest of the
    enum initialisation. Bytes [26, 32) are my initialisation.

    Clearly the enum constants are created first. Which is useful if I
    wanted any static finals involving the enumeration that they are in.

    Tom Hawtin
    --
    Unemployed English Java programmer
    http://jroller.com/page/tackline/
    Thomas Hawtin, Aug 26, 2005
    #8
  9. Roedy Green

    Roedy Green Guest

    On Fri, 26 Aug 2005 19:06:09 GMT, Roedy Green
    <> wrote or quoted :

    > private static final int x = Math.min(2,1);


    what term do you use to distinguish been a constant like this:

    private static final int X = Math.min(2,1);

    and like this:

    private static final int Y = 42;


    The second is known at compile time, can be used in case labels, is
    in-lineable.
    --
    Canadian Mind Products, Roedy Green.
    http://mindprod.com Again taking new Java programming contracts.
    Roedy Green, Aug 26, 2005
    #9
  10. Roedy Green wrote:
    >
    > Here is your code modified slightly to make it compile:
    > [...]
    > Here is how it decompiles:
    > [...]
    > You can see that had the i=x been allowed, it should have been fine.
    > The constructors are run after the x = Math.min(2,1) are they not?


    I don't think you should depend upon your decompiler being accurate.
    javap -c is the way to go. You can see that the actual byte code is
    different from what your decompiler is claiming.

    Tom Hawtin
    --
    Unemployed English Java programmer
    http://jroller.com/page/tackline/
    Thomas Hawtin, Aug 26, 2005
    #10
  11. Roedy Green wrote:
    >
    > what term do you use to distinguish been a constant like this:
    >
    > private static final int X = Math.min(2,1);
    >
    > and like this:
    >
    > private static final int Y = 42;
    >
    >
    > The second is known at compile time, can be used in case labels, is
    > in-lineable.


    The first is really just a static final, colloquially known as a
    constant. The latter is a "compile-time constant expression" according
    to the JLS 3rd Ed, 15.28, p525. I believe the second edition has some
    errata in that section about the use (or not) of nulls.


    There's some non-normative discussion about enum static initialisation
    in section 8.9 (Enums!), p252.

    Tom Hawtin
    --
    Unemployed English Java programmer
    http://jroller.com/page/tackline/
    Thomas Hawtin, Aug 26, 2005
    #11
  12. Roedy Green

    Roedy Green Guest

    On Fri, 26 Aug 2005 20:39:17 +0100, Thomas Hawtin
    <> wrote or quoted :

    >I don't think you should depend upon your decompiler being accurate.
    >javap -c is the way to go. You can see that the actual byte code is
    >different from what your decompiler is claiming.


    You are such a clever character. You are right yet again. The
    compiler indeed does the static field inits AFTER the constructor
    calls. If it reversed the order, the way DJ decompiles it, Java would
    have no problem with static references in constructors.

    Here is the Javap decompilation of Final.java


    javap -c Final

    Compiled from "Final.java"
    final class Final extends java.lang.Enum{
    public static final Final X;

    public static final Final[] values();
    Code:
    0: getstatic #1; //Field $VALUES:[LFinal;
    3: invokevirtual #2; //Method
    "[LFinal;".clone:()Ljava/lang/Object;
    6: checkcast #3; //class "[LFinal;"
    9: areturn

    public static Final valueOf(java.lang.String);
    Code:
    0: ldc_w #4; //class Final
    3: aload_0
    4: invokestatic #5; //Method
    java/lang/Enum.valueOf:(Ljava/lang/Class;Lj
    ava/lang/String;)Ljava/lang/Enum;
    7: checkcast #4; //class Final
    10: areturn

    static {};
    Code:
    0: new #4; //class Final
    3: dup
    4: ldc #7; //String X
    6: iconst_0
    7: invokespecial #8; //Method "<init>":(Ljava/lang/String;I)V
    10: putstatic #9; //Field X:LFinal;
    13: iconst_1
    14: anewarray #4; //class Final
    17: dup
    18: iconst_0
    19: getstatic #9; //Field X:LFinal;
    22: aastore
    23: putstatic #1; //Field $VALUES:[LFinal;
    26: iconst_2
    27: iconst_1
    28: invokestatic #10; //Method java/lang/Math.min:(II)I
    31: putstatic #11; //Field x:I
    34: return

    }























































































































































































    --
    Canadian Mind Products, Roedy Green.
    http://mindprod.com Again taking new Java programming contracts.
    Roedy Green, Aug 26, 2005
    #12
  13. Roedy Green wrote:
    > On Fri, 26 Aug 2005 20:39:17 +0100, Thomas Hawtin
    > <> wrote or quoted :
    >
    >
    >>I don't think you should depend upon your decompiler being accurate.
    >>javap -c is the way to go. You can see that the actual byte code is
    >>different from what your decompiler is claiming.

    >
    >
    > You are such a clever character. You are right yet again. The
    > compiler indeed does the static field inits AFTER the constructor
    > calls. If it reversed the order, the way DJ decompiles it, Java would
    > have no problem with static references in constructors.
    >


    But there would be problems with static references to the enum values,
    don't you think? So this may be a damned if do you, damned if you don't
    type situation.

    Ray

    --
    XML is the programmer's duct tape.
    Raymond DeCampo, Aug 27, 2005
    #13
  14. Roedy Green

    Roedy Green Guest

    On Sat, 27 Aug 2005 01:40:26 GMT, Raymond DeCampo
    <> wrote or quoted :

    >But there would be problems with static references to the enum values,
    >don't you think? So this may be a damned if do you, damned if you don't
    >type situation.

    Ah ha!

    To solve that one you would need some sort of spread-sheet like
    natural ordering of the initialisations which would be very
    unJava-like.

    But then you'd think you could SET static values in constructors, just
    not reference them. Perhaps that is how it does work.

    I also want to recheck what their thinking is on compile time static
    constants.

    I suppose you get into similar situation if you have a pair of classes
    whose static inits cross reference each other. It is just that Java
    does not warn you of the problems.
    --
    Canadian Mind Products, Roedy Green.
    http://mindprod.com Again taking new Java programming contracts.
    Roedy Green, Aug 28, 2005
    #14
  15. Roedy Green

    Roedy Green Guest

    On Fri, 26 Aug 2005 20:51:14 +0100, Thomas Hawtin
    <> wrote or quoted :

    >constant. The latter is a "compile-time constant expression" according
    >to the JLS 3rd Ed, 15.28, p525. I believe the second edition has some
    >errata in that section about the use (or not) of nulls.


    I have written an essay on what I have discovered about constants.
    Other that the usual error checking, I invite you to submit some
    counter-intuitive examples on what is compile-time and what is a
    load-time constant.

    see http://mindprod.com/jgloss/constants.html
    --
    Canadian Mind Products, Roedy Green.
    http://mindprod.com Again taking new Java programming contracts.
    Roedy Green, Aug 28, 2005
    #15
    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. No One

    Vanishing buttons mystery "solved"

    No One, Oct 28, 2004, in forum: ASP .Net
    Replies:
    0
    Views:
    323
    No One
    Oct 28, 2004
  2. -

    enum within an enum

    -, Jun 12, 2005, in forum: Java
    Replies:
    6
    Views:
    526
  3. Jerminia
    Replies:
    3
    Views:
    615
    Roedy Green
    Oct 7, 2005
  4. Michael Bacarella
    Replies:
    26
    Views:
    1,320
    harri
    Nov 20, 2007
  5. Al Reynolds

    Bizarre JS brackets bug - mystery solved!

    Al Reynolds, Sep 30, 2004, in forum: Javascript
    Replies:
    8
    Views:
    108
    Jim Ley
    Oct 1, 2004
Loading...

Share This Page