Search code examples
groovy

Groovy List<List<Integer>> behaving like List<Integer>


I discovered in a production code a casting from List<List<Integer>> to List<Integer> which does not make any sense, but it's working without any problem being raised, so I wrote a quick test to check it out and to my surprise a List<List<Object>> can behave like a List<Object>

    static void main(String[] args) {
        List<List<Integer>> list = []
        list.add(1)
        println(list.get(0))
        println(list.get(0).getClass())
    }

the output is:

    1
    class java.lang.Integer

I understand that we can define lists containing values of heterogeneous types like def heterogeneous = [1, "a", true], but in the example above the static typing clearly indicates that it should be List<List<Integer>>

Any explanation of the weird behavior ?


Solution

  • In straight-forward Groovy code, the type checking of Lists or other generic typed objects is weak.

    Meaning that a List<List<Integer>> can be fed with any objects, like Strings, integers or arrays.

    To make the type checking real, you should use @CompileStatic or @TypeChecked annotations like so:

    @groovy.transform.CompileStatic
    def getInts(){
      List<List<Integer>> ints = []
      ints << 1
      ints << [ '2' ]
      ints << [ 3 ]
    }
    
    getInts()
    

    The code above throws 2 compilation errors:

    startup failed:
    Script1.groovy: 4: [Static type checking] - Cannot call <T> java.util.List <List>#leftShift(T) with arguments [int] 
     @ line 4, column 3.
         ints << 1
         ^
    
    Script1.groovy: 5: [Static type checking] - Cannot call <T> java.util.List <List>#leftShift(T) with arguments [java.util.List <java.lang.String>] 
     @ line 5, column 3.
         ints << [ '2' ]
         ^
    
    2 errors
    

    so that only the line ints << [ 3 ] is valid.