You are viewing a plain text version of this content. The canonical link for it is here.
Posted to users@groovy.apache.org by Per Nyfelt <Pe...@nordnet.se> on 2025/04/14 07:37:34 UTC

putAt and @CompileStatic

Hi,
I was recently experimenting with @CompileStatic for performance improvements and noticed that when using the short notation of putAt to assign a null value I get the following error
[Static type checking] - Cannot call <T> org.codehaus.groovy.runtime.DefaultGroovyMethods#putAt(java.util.List<T>, int, T) with arguments [java.util.List<java.lang.String>, int, java.lang.Object]

Here is an example:

import groovy.transform.CompileStatic
import org.junit.jupiter.api.Test

@CompileStatic
class PutAtTest {

  @Test
  void testList() {
    // These all work
    def list = ['a', 'b', 'c']
    list[0] = 'aa'
    assert list[0] == 'aa'

    list.set(2, null)
    assert list[2] == null

    list.putAt(0, null)
    assert list[0] == null

    // This Fails
    list[1] = null
    assert list[1] == null : "Short notation not working when assigning null"
  }

  @Test
  void testMap() {
    // These all work
    def map = [a: 'foo', b: 'bar', c: 'baz']
    map['a'] = 'aa'
    assert map['a'] == 'aa'

    map.put('c', null)
    assert map['c'] == null

    map.putAt('a', null)
    assert map['a'] == null

    // This Fails
    map['b'] = null
    assert map['b'] == null  : "Short notation not working when assigning null"
  }
}

Is this expected behavior or a bug? Does anyone know of workaround?

Regards,
Per

Re: putAt and @CompileStatic

Posted by Per Nyfelt <Pe...@nordnet.se>.
I created https://issues.apache.org/jira/browse/GROOVY-11621 for this. 

On 2025-04-14, 13:09, "Per Nyfelt" <Per.Nyfelt@nordnet.se <ma...@nordnet.se>> wrote:


Oh, and property assignment with null for maps works with @CompileStatic e.g.
def map = [a: 'foo', b: 'bar', c: 'baz']
map.b = null
assert map['b'] == null


I would assume we want bracket notation and property notation to be functionally equivalent right?


On 2025-04-14, 11:37, "Per Nyfelt" <Per.Nyfelt@nordnet.se <ma...@nordnet.se> <mailto:Per.Nyfelt@nordnet.se <ma...@nordnet.se>>> wrote:




Thank you for the workaround!




For a slightly more complex case of overriding putAt I found that I can do like this if I also provide a getAt which might be of interest for others trying to do similar things:




class TwoD {
List<List<?>> rows = []




TwoD(List rows) {
this.rows = rows
}




def putAt(List list, Object value) {
println "putAt: $list : $value"
Integer rowIdx = list[0] as Integer
Integer colIdx = list[1] as Integer
// Must add parenthesis for assignment to work 
(rows[rowIdx][colIdx]) = value
}




// This is never called in putAt but when compiling static, it must exists for it to compile, otherwise we get the following compilation error:
// [Static type checking] - Cannot find matching method PutAtTest$TwoD#getAt(java.util.List<E>). Please check if the declared type is correct and if the method exists.
def getAt(List where) {
println "getAt: $where"
Integer rowIdx = where[0] as Integer
Integer colIdx = where[1] as Integer
return rows[rowIdx][colIdx]
}




String toString() {
StringBuilder sb = new StringBuilder()
rows.each { List row ->
row.each {sb.append(it).append(', ')}
sb.append('\n')
}
sb
}
}




@CompileStatic
@Test
void testOverride() {
def d = new TwoD([[1,2,3], ['a', 'b', 'c']])
println d
d[0,1] = null
println d
assert d[0,1] == null
assert d[1,2] == 'c'
}




Which will output
1, 2, 3, 
a, b, c, 




putAt: [0, 1] : null
1, null, 3, 
a, b, c, 




getAt: [0, 1]
getAt: [1, 2]




Regards,
Per




On 2025-04-14, 10:57, "Paul King" <paulk@asert.com.au <ma...@asert.com.au> <mailto:paulk@asert.com.au <ma...@asert.com.au>> <mailto:paulk@asert.com.au <ma...@asert.com.au> <mailto:paulk@asert.com.au <ma...@asert.com.au>>>> wrote:








A workaround is to cast, e.g.








list[1] = (String) null








I don't see why that is needed for null. On GROOVY_4_0_X, the map also
needs the cast. On master, only the list needs the cast. I can't
explain that either (yet).








Paul.








On Mon, Apr 14, 2025 at 5:37 PM Per Nyfelt <Per.Nyfelt@nordnet.se <ma...@nordnet.se> <mailto:Per.Nyfelt@nordnet.se <ma...@nordnet.se>> <mailto:Per.Nyfelt@nordnet.se <ma...@nordnet.se> <mailto:Per.Nyfelt@nordnet.se <ma...@nordnet.se>>>> wrote:
>
> Hi,
>
> I was recently experimenting with @CompileStatic for performance improvements and noticed that when using the short notation of putAt to assign a null value I get the following error
>
> [Static type checking] - Cannot call <T> org.codehaus.groovy.runtime.DefaultGroovyMethods#putAt(java.util.List<T>, int, T) with arguments [java.util.List<java.lang.String>, int, java.lang.Object]
>
>
>
> Here is an example:
>
>
>
> import groovy.transform.CompileStatic
> import org.junit.jupiter.api.Test
>
> @CompileStatic
> class PutAtTest {
>
> @Test
> void testList() {
> // These all work
> def list = ['a', 'b', 'c']
> list[0] = 'aa'
> assert list[0] == 'aa'
>
> list.set(2, null)
> assert list[2] == null
>
> list.putAt(0, null)
> assert list[0] == null
>
> // This Fails
> list[1] = null
> assert list[1] == null : "Short notation not working when assigning null"
> }
>
> @Test
> void testMap() {
> // These all work
> def map = [a: 'foo', b: 'bar', c: 'baz']
> map['a'] = 'aa'
> assert map['a'] == 'aa'
>
> map.put('c', null)
> assert map['c'] == null
>
> map.putAt('a', null)
> assert map['a'] == null
>
> // This Fails
> map['b'] = null
> assert map['b'] == null : "Short notation not working when assigning null"
> }
> }
>
>
>
> Is this expected behavior or a bug? Does anyone know of workaround?
>
>
>
> Regards,
>
> Per






















Re: putAt and @CompileStatic

Posted by Per Nyfelt <Pe...@nordnet.se>.
Oh, and property assignment with null for maps works with @CompileStatic e.g.
def map = [a: 'foo', b: 'bar', c: 'baz']
map.b = null
assert map['b'] == null

I would assume we want bracket notation and property notation to be functionally equivalent right?

On 2025-04-14, 11:37, "Per Nyfelt" <Per.Nyfelt@nordnet.se <ma...@nordnet.se>> wrote:


Thank you for the workaround!


For a slightly more complex case of overriding putAt I found that I can do like this if I also provide a getAt which might be of interest for others trying to do similar things:


class TwoD {
List<List<?>> rows = []


TwoD(List rows) {
this.rows = rows
}


def putAt(List list, Object value) {
println "putAt: $list : $value"
Integer rowIdx = list[0] as Integer
Integer colIdx = list[1] as Integer
// Must add parenthesis for assignment to work 
(rows[rowIdx][colIdx]) = value
}


// This is never called in putAt but when compiling static, it must exists for it to compile, otherwise we get the following compilation error:
// [Static type checking] - Cannot find matching method PutAtTest$TwoD#getAt(java.util.List<E>). Please check if the declared type is correct and if the method exists.
def getAt(List where) {
println "getAt: $where"
Integer rowIdx = where[0] as Integer
Integer colIdx = where[1] as Integer
return rows[rowIdx][colIdx]
}


String toString() {
StringBuilder sb = new StringBuilder()
rows.each { List row ->
row.each {sb.append(it).append(', ')}
sb.append('\n')
}
sb
}
}


@CompileStatic
@Test
void testOverride() {
def d = new TwoD([[1,2,3], ['a', 'b', 'c']])
println d
d[0,1] = null
println d
assert d[0,1] == null
assert d[1,2] == 'c'
}


Which will output
1, 2, 3, 
a, b, c, 


putAt: [0, 1] : null
1, null, 3, 
a, b, c, 


getAt: [0, 1]
getAt: [1, 2]


Regards,
Per


On 2025-04-14, 10:57, "Paul King" <paulk@asert.com.au <ma...@asert.com.au> <mailto:paulk@asert.com.au <ma...@asert.com.au>>> wrote:




A workaround is to cast, e.g.




list[1] = (String) null




I don't see why that is needed for null. On GROOVY_4_0_X, the map also
needs the cast. On master, only the list needs the cast. I can't
explain that either (yet).




Paul.




On Mon, Apr 14, 2025 at 5:37 PM Per Nyfelt <Per.Nyfelt@nordnet.se <ma...@nordnet.se> <mailto:Per.Nyfelt@nordnet.se <ma...@nordnet.se>>> wrote:
>
> Hi,
>
> I was recently experimenting with @CompileStatic for performance improvements and noticed that when using the short notation of putAt to assign a null value I get the following error
>
> [Static type checking] - Cannot call <T> org.codehaus.groovy.runtime.DefaultGroovyMethods#putAt(java.util.List<T>, int, T) with arguments [java.util.List<java.lang.String>, int, java.lang.Object]
>
>
>
> Here is an example:
>
>
>
> import groovy.transform.CompileStatic
> import org.junit.jupiter.api.Test
>
> @CompileStatic
> class PutAtTest {
>
> @Test
> void testList() {
> // These all work
> def list = ['a', 'b', 'c']
> list[0] = 'aa'
> assert list[0] == 'aa'
>
> list.set(2, null)
> assert list[2] == null
>
> list.putAt(0, null)
> assert list[0] == null
>
> // This Fails
> list[1] = null
> assert list[1] == null : "Short notation not working when assigning null"
> }
>
> @Test
> void testMap() {
> // These all work
> def map = [a: 'foo', b: 'bar', c: 'baz']
> map['a'] = 'aa'
> assert map['a'] == 'aa'
>
> map.put('c', null)
> assert map['c'] == null
>
> map.putAt('a', null)
> assert map['a'] == null
>
> // This Fails
> map['b'] = null
> assert map['b'] == null : "Short notation not working when assigning null"
> }
> }
>
>
>
> Is this expected behavior or a bug? Does anyone know of workaround?
>
>
>
> Regards,
>
> Per










Re: putAt and @CompileStatic

Posted by Per Nyfelt <Pe...@nordnet.se>.
Thank you for the workaround!

For a slightly more complex case of overriding putAt I found that I can do like this if I also provide a getAt which might be of interest for others trying to do similar things:

class TwoD {
    List<List<?>> rows = []

    TwoD(List rows) {
      this.rows = rows
    }

    def putAt(List list, Object value) {
      println "putAt: $list : $value"
      Integer rowIdx = list[0] as Integer
      Integer colIdx = list[1] as Integer
      // Must add parenthesis for assignment to work 
      (rows[rowIdx][colIdx]) = value
    }

    // This is never called in putAt but when compiling static, it must exists for it to compile, otherwise we get the following compilation error:
    // [Static type checking] - Cannot find matching method PutAtTest$TwoD#getAt(java.util.List<E>). Please check if the declared type is correct and if the method exists.
    def getAt(List where) {
      println "getAt: $where"
      Integer rowIdx = where[0] as Integer
      Integer colIdx = where[1] as Integer
      return rows[rowIdx][colIdx]
    }

    String toString() {
      StringBuilder sb = new StringBuilder()
      rows.each {  List row ->
        row.each {sb.append(it).append(', ')}
        sb.append('\n')
      }
      sb
    }
  }

  @CompileStatic
  @Test
  void testOverride() {
    def d = new TwoD([[1,2,3], ['a', 'b', 'c']])
    println d
    d[0,1] = null
    println d
    assert d[0,1] == null
    assert d[1,2] == 'c'
  }

Which will output
1, 2, 3, 
a, b, c, 

putAt: [0, 1] : null
1, null, 3, 
a, b, c, 

getAt: [0, 1]
getAt: [1, 2]

Regards,
Per

On 2025-04-14, 10:57, "Paul King" <paulk@asert.com.au <ma...@asert.com.au>> wrote:


A workaround is to cast, e.g.


list[1] = (String) null


I don't see why that is needed for null. On GROOVY_4_0_X, the map also
needs the cast. On master, only the list needs the cast. I can't
explain that either (yet).


Paul.


On Mon, Apr 14, 2025 at 5:37 PM Per Nyfelt <Per.Nyfelt@nordnet.se <ma...@nordnet.se>> wrote:
>
> Hi,
>
> I was recently experimenting with @CompileStatic for performance improvements and noticed that when using the short notation of putAt to assign a null value I get the following error
>
> [Static type checking] - Cannot call <T> org.codehaus.groovy.runtime.DefaultGroovyMethods#putAt(java.util.List<T>, int, T) with arguments [java.util.List<java.lang.String>, int, java.lang.Object]
>
>
>
> Here is an example:
>
>
>
> import groovy.transform.CompileStatic
> import org.junit.jupiter.api.Test
>
> @CompileStatic
> class PutAtTest {
>
> @Test
> void testList() {
> // These all work
> def list = ['a', 'b', 'c']
> list[0] = 'aa'
> assert list[0] == 'aa'
>
> list.set(2, null)
> assert list[2] == null
>
> list.putAt(0, null)
> assert list[0] == null
>
> // This Fails
> list[1] = null
> assert list[1] == null : "Short notation not working when assigning null"
> }
>
> @Test
> void testMap() {
> // These all work
> def map = [a: 'foo', b: 'bar', c: 'baz']
> map['a'] = 'aa'
> assert map['a'] == 'aa'
>
> map.put('c', null)
> assert map['c'] == null
>
> map.putAt('a', null)
> assert map['a'] == null
>
> // This Fails
> map['b'] = null
> assert map['b'] == null : "Short notation not working when assigning null"
> }
> }
>
>
>
> Is this expected behavior or a bug? Does anyone know of workaround?
>
>
>
> Regards,
>
> Per




Re: putAt and @CompileStatic

Posted by Paul King <pa...@asert.com.au>.
A workaround is to cast, e.g.

list[1] = (String) null

I don't see why that is needed for null. On GROOVY_4_0_X, the map also
needs the cast. On master, only the list needs the cast. I can't
explain that either (yet).

Paul.

On Mon, Apr 14, 2025 at 5:37 PM Per Nyfelt <Pe...@nordnet.se> wrote:
>
> Hi,
>
> I was recently experimenting with @CompileStatic for performance improvements and noticed that when using the short notation of putAt to assign a null value I get the following error
>
> [Static type checking] - Cannot call <T> org.codehaus.groovy.runtime.DefaultGroovyMethods#putAt(java.util.List<T>, int, T) with arguments [java.util.List<java.lang.String>, int, java.lang.Object]
>
>
>
> Here is an example:
>
>
>
> import groovy.transform.CompileStatic
> import org.junit.jupiter.api.Test
>
> @CompileStatic
> class PutAtTest {
>
>   @Test
>   void testList() {
>     // These all work
>     def list = ['a', 'b', 'c']
>     list[0] = 'aa'
>     assert list[0] == 'aa'
>
>     list.set(2, null)
>     assert list[2] == null
>
>     list.putAt(0, null)
>     assert list[0] == null
>
>     // This Fails
>     list[1] = null
>     assert list[1] == null : "Short notation not working when assigning null"
>   }
>
>   @Test
>   void testMap() {
>     // These all work
>     def map = [a: 'foo', b: 'bar', c: 'baz']
>     map['a'] = 'aa'
>     assert map['a'] == 'aa'
>
>     map.put('c', null)
>     assert map['c'] == null
>
>     map.putAt('a', null)
>     assert map['a'] == null
>
>     // This Fails
>     map['b'] = null
>     assert map['b'] == null  : "Short notation not working when assigning null"
>   }
> }
>
>
>
> Is this expected behavior or a bug? Does anyone know of workaround?
>
>
>
> Regards,
>
> Per