Uploaded image for project: 'Groovy'
  1. Groovy
  2. GROOVY-8561

Support java-like array

    XMLWordPrintableJSON

Details

    • New Feature
    • Status: Closed
    • Major
    • Resolution: Won't Fix
    • None
    • None
    • None
    • None

    Description

      Groovy currently "promotes" a singleton instance of an object into an array for assignments, e.g.:

      Integer[] nums = 42
      assert nums instanceof Integer[]
      assert nums.size() == 1
      assert nums[0] instanceof Integer
      

      This aligns with how Groovy behaves if you try to call `.each{}` on a non-aggregate. It treats it like a singleton collection and "iterates" over the one item.

      The existing behavior also currently works for singleton Closures:

      Closure[] fns0 = { }
      assert fns0 instanceof Closure[]
      assert fns0.size() == 1
      assert fns0[0] instanceof Closure
      

      To add support for Java array notation, we will need to partially disable this behavior. The proposed change involves smart parsing, e.g. it will distinguish cases which must be an array and cases which must be a closure but there are some degenerate edge cases which will become breaking changes.

      The case with the empty closure above will no longer work, instead you will get this behavior, i.e. an empty array is given precedence over an empty closure:

      Closure[] fns1 = { }
      assert fns1 instanceof Closure[]
      assert fns1.size() == 0
      

      To get the old behavior back you have a couple of options. Firstly, you can provide the explicit closure argument delimiter:

      Closure[] fns2 = { -> } // can't be an array
      assert fns2 instanceof Closure[]
      assert fns2.size() == 1
      assert fns2[0] instanceof Closure
      

      Or don't rely on singleton promotion and explicitly provide also the array curly braces:

      Closure[] fns3 = { { } }
      assert fns3 instanceof Closure[]
      assert fns3.size() == 1
      assert fns3[0] instanceof Closure
      

      Similarly, for the case of the identity closure:

      Closure[] fns4 = { it }
      

      Previously this worked but under this proposal will give:

      groovy.lang.MissingPropertyException: No such property: it ...
      

      Your options are to add the extra array braces as per above, or use explicit params, e.g.:

      Closure[] fns5 = { it -> it }
      assert fns5 instanceof Closure[]
      assert fns5.size() == 1
      assert fns5[0] instanceof Closure
      

      Alternatively, for this special case you have the following additional option:

      Closure[] fns6 = Closure.IDENTITY
      assert fns6 instanceof Closure[]
      assert fns6.size() == 1
      assert fns6[0] instanceof Closure
      

      There are other cases as well, e.g. this code which currently creates a closure array containing a closure returning the integer 0:

      Closure[] fns7 = { 0 }
      

      will no longer be supported and will fail with:

      org.codehaus.groovy.runtime.typehandling.GroovyCastException: Cannot cast object '0' with class 'java.lang.Integer' to class 'groovy.lang.Closure'
      

      The solutions are similar to previously (explicit delimiter):

      Closure[] fns8 = { -> 0 }
      

      or (explicit outer array braces):

      Closure[] fns9 = { { 0 } }
      

      Here is the PR: https://github.com/apache/groovy/pull/691

      Attachments

        Issue Links

          Activity

            People

              daniel_sun Daniel Sun
              daniel_sun Daniel Sun
              Votes:
              0 Vote for this issue
              Watchers:
              2 Start watching this issue

              Dates

                Created:
                Updated:
                Resolved: