groovy
  1. groovy
  2. GROOVY-4734

Object initializer not being called on object construction

    Details

    • Type: Bug Bug
    • Status: Closed Closed
    • Priority: Critical Critical
    • Resolution: Fixed
    • Affects Version/s: 1.8-rc-2
    • Fix Version/s: 1.8-rc-4
    • Component/s: None
    • Labels:
      None
    • Number of attachments :
      0

      Description

      Perhaps related to GROOVY-4733.

      I would expect that this code prints "99", but it doesn't. It prints "9":

      class A {
              {
                      print f
      		}
              def f = 9 //
      		def other = { }
      		{
              	print f }
      }
      new A()
      

      This code, however, prints "99":

      class A {
              {
                      print f
      		}
      		{
              	print f }
              def f = 9 //
      		def other = { }
      }
      new A()
      

        Activity

        Hide
        Andrew Eisenberg added a comment -

        This problem does not occur in 1.7.

        Show
        Andrew Eisenberg added a comment - This problem does not occur in 1.7.
        Hide
        CÚdric Champeau added a comment -

        This doesn't look like a bug to me : forward references are forbidden in initialization blocks. At least, the compiler should have failed in both cases.

        Show
        CÚdric Champeau added a comment - This doesn't look like a bug to me : forward references are forbidden in initialization blocks. At least, the compiler should have failed in both cases.
        Hide
        Andrew Eisenberg added a comment -

        This is not really a forward reference. Since the class knows all of its fields at load time, the field can be referenced in a constructor or initializer lexically before it is declared.

        Eg, this is legal in Java:

        public class C {
            static {
                f = 0;
            }
            static int f;
            {
                f2 = 0;
            }
            int f2;
        }
        

        Also, the code above has worked in the 1.7 stream, so something must have changed.

        Show
        Andrew Eisenberg added a comment - This is not really a forward reference. Since the class knows all of its fields at load time, the field can be referenced in a constructor or initializer lexically before it is declared. Eg, this is legal in Java: public class C { static { f = 0; } static int f; { f2 = 0; } int f2; } Also, the code above has worked in the 1.7 stream, so something must have changed.
        Hide
        CÚdric Champeau added a comment -

        Your last snippet in Java is valid because it's a field initialization. But this would fail (and it's the same as your report) :

        static {
                System.out.println(f);
            }
            static int f = 0;
        
        Show
        CÚdric Champeau added a comment - Your last snippet in Java is valid because it's a field initialization. But this would fail (and it's the same as your report) : static { System .out.println(f); } static int f = 0;
        Hide
        Andrew Eisenberg added a comment -

        Try running this in Java 1.5:

        public class C {
            static {
                f = 2;
            }
            static int f;
            {
                f2 = 2;
            }
            int f2;
            public static void main(String[] args) {
                System.out.println(f);
                System.out.println(new C().f2);
            }
        }
        

        I expect that you would get:

        2
        2
        

        If you don't, then I would be surprised (since that is what I got when I ran the program).

        Even if, as you mention, static initializers cannot contain forward references (which it seems like they can), the original problem was about object initializers.

        This is a regression from Groovy 1.7.x, which allowed this kind of code.

        Show
        Andrew Eisenberg added a comment - Try running this in Java 1.5: public class C { static { f = 2; } static int f; { f2 = 2; } int f2; public static void main( String [] args) { System .out.println(f); System .out.println( new C().f2); } } I expect that you would get: 2 2 If you don't, then I would be surprised (since that is what I got when I ran the program). Even if, as you mention, static initializers cannot contain forward references (which it seems like they can), the original problem was about object initializers. This is a regression from Groovy 1.7.x, which allowed this kind of code.
        Hide
        CÚdric Champeau added a comment -

        mmm, can't see any regression here. Using Groovy 1.8-rc3, there's no problem with your latest example, it works as expected. I'm still thinking your original problem is that you had println statements which were indeed forward references.

        public class C {
            static {
                f = 2;
            }
            static int f;
            {
                f2 = 3;
            }
            int f2;
            public static void main(String[] args) {
                System.out.println(org.codehaus.groovy.runtime.InvokerHelper.getVersion())
                System.out.println(f);
                System.out.println(new C().f2);
            }
        }
        

        Outputs :

        1.8.0-rc-3
        2
        3
        
        Show
        CÚdric Champeau added a comment - mmm, can't see any regression here. Using Groovy 1.8-rc3, there's no problem with your latest example, it works as expected. I'm still thinking your original problem is that you had println statements which were indeed forward references. public class C { static { f = 2; } static int f; { f2 = 3; } int f2; public static void main( String [] args) { System .out.println(org.codehaus.groovy.runtime.InvokerHelper.getVersion()) System .out.println(f); System .out.println( new C().f2); } } Outputs : 1.8.0-rc-3 2 3
        Hide
        Andrew Eisenberg added a comment -

        Cedric,

        I don't think you tried my original example. Here is a simpler way to recreate the problem.

        Execute this in RC3:

        class A {
          { f }
          def f = 9
          { f }
        }
        new A()
        

        This exception:

        Caught: groovy.lang.MissingMethodException: No signature of method: java.lang.Integer.call() is applicable for argument types: (A$_closure1) values: [A$_closure1@4fa4ded3]
        Possible solutions: wait(), abs(), any(), wait(long), any(groovy.lang.Closure), each(groovy.lang.Closure)
        	at A.<init>(a.groovy:3)
        	at a.run(a.groovy:7)
        

        Now comment out the second initializer and there is no exception:

        class A {
          { f }
          def f = 9
          // { f }
        }
        new A()
        

        Same forward reference each time. Different behavior.

        Show
        Andrew Eisenberg added a comment - Cedric, I don't think you tried my original example. Here is a simpler way to recreate the problem. Execute this in RC3: class A { { f } def f = 9 { f } } new A() This exception: Caught: groovy.lang.MissingMethodException: No signature of method: java.lang. Integer .call() is applicable for argument types: (A$_closure1) values: [A$_closure1@4fa4ded3] Possible solutions: wait(), abs(), any(), wait( long ), any(groovy.lang.Closure), each(groovy.lang.Closure) at A.<init>(a.groovy:3) at a.run(a.groovy:7) Now comment out the second initializer and there is no exception: class A { { f } def f = 9 // { f } } new A() Same forward reference each time. Different behavior.
        Hide
        Andrew Eisenberg added a comment -

        BTW- This is not exactly the same problem described at the top of this bug report, but it is probably related. This is the issue I raised in GROOVY-4733.

        Show
        Andrew Eisenberg added a comment - BTW- This is not exactly the same problem described at the top of this bug report, but it is probably related. This is the issue I raised in GROOVY-4733 .
        Hide
        CÚdric Champeau added a comment -

        Ok, my understanding of your last example code is that it shouldn't compile in both cases. So the bug is, to me, that we allow forward references where we shouldn't.

        {f}
        def f = 9
        

        is a forward reference (since no value is set, it only returns the value of the f field).

        Show
        CÚdric Champeau added a comment - Ok, my understanding of your last example code is that it shouldn't compile in both cases. So the bug is, to me, that we allow forward references where we shouldn't. {f} def f = 9 is a forward reference (since no value is set, it only returns the value of the f field).
        Hide
        Andrew Eisenberg added a comment -

        Why are you proposing that Groovy should disallow references as in the example I provide above? I know that Groovy is not Java, but disallowing such references would be an unnecessary departure (IMHO) from Java semantics.

        Despite what you said earlier, forward references in Java initializer blocks are allowed. Why should Groovy be any different?

        Show
        Andrew Eisenberg added a comment - Why are you proposing that Groovy should disallow references as in the example I provide above? I know that Groovy is not Java, but disallowing such references would be an unnecessary departure (IMHO) from Java semantics. Despite what you said earlier, forward references in Java initializer blocks are allowed. Why should Groovy be any different?
        Hide
        CÚdric Champeau added a comment -

        From what I read here, yes, the example you provide should not compile, neither in Java nor Groovy : http://java.sun.com/docs/books/jls/second_edition/html/classes.doc.html#287406

        Show
        CÚdric Champeau added a comment - From what I read here, yes, the example you provide should not compile, neither in Java nor Groovy : http://java.sun.com/docs/books/jls/second_edition/html/classes.doc.html#287406
        Hide
        Andrew Eisenberg added a comment - - edited

        Ummmm....yes. This does compile (in Java):

        class Test {
          { j = 9; }	
          int j = 1;
        }
        

        And this:

        class Test {
          static { j = 9; }	
          static int j = 1;
        }
        

        However, you are correct that this does not compile:

        class Test {
          int i = j;
          int j = 1;
        }
        
        Show
        Andrew Eisenberg added a comment - - edited Ummmm....yes. This does compile (in Java): class Test { { j = 9; } int j = 1; } And this: class Test { static { j = 9; } static int j = 1; } However, you are correct that this does not compile: class Test { int i = j; int j = 1; }
        Hide
        Andrew Eisenberg added a comment -

        Let me rephrase my previous comment.

        class Test {
          { j = 9; }	
          int j = 1;
        }
        

        works but only because the j variable is assigned but not used.

        This, however, does not work:

        class Test {
          { int i = j; }	
          int j = 1;
        }
        

        I am very surprised that Javac does not flag the first program as an error.

        I think the best thing to do right now is for me to come up with a very good example of what the problem is. I feel that we have gone off track here while we talk about initializers.

        Show
        Andrew Eisenberg added a comment - Let me rephrase my previous comment. class Test { { j = 9; } int j = 1; } works but only because the j variable is assigned but not used. This, however, does not work: class Test { { int i = j; } int j = 1; } I am very surprised that Javac does not flag the first program as an error. I think the best thing to do right now is for me to come up with a very good example of what the problem is. I feel that we have gone off track here while we talk about initializers.
        Hide
        Andrew Eisenberg added a comment -

        It seems that the real problem here was reported (and fixed) under GROOVY-4747. The other problems that I saw were actually confusions regarding how initializers work in Java and how Groovy parses code blocks. Thanks for your help in getting me to understand this.

        Show
        Andrew Eisenberg added a comment - It seems that the real problem here was reported (and fixed) under GROOVY-4747 . The other problems that I saw were actually confusions regarding how initializers work in Java and how Groovy parses code blocks. Thanks for your help in getting me to understand this.
        Andrew Eisenberg made changes -
        Field Original Value New Value
        Status Open [ 1 ] Resolved [ 5 ]
        Fix Version/s 1.8-rc-4 [ 17245 ]
        Resolution Fixed [ 1 ]
        Paul King made changes -
        Status Resolved [ 5 ] Closed [ 6 ]

          People

          • Assignee:
            Unassigned
            Reporter:
            Andrew Eisenberg
          • Votes:
            0 Vote for this issue
            Watchers:
            0 Start watching this issue

            Dates

            • Created:
              Updated:
              Resolved: