Question 1
Is it irrelevant whether a List (list of objects) or a List (list of Strings) is used in Groovy?
When running Groovy "normally", generics are thrown away before compilation, so only exist in the source as helpful reminders to the developer.
However, you can use @CompileStatic or @TypeChecked to make Groovy honour these Generics and check the types of things at compilation.
As an example, consider I have the following project structure:
project
|---- src
| |---- main
| |---- groovy
| |---- test
| |---- ListDelegate.groovy
| |---- Main.groovy
|---- build.gradle
With the code:
build.gradle
apply plugin: 'groovy'
repositories {
mavenCentral()
}
dependencies {
compile 'org.codehaus.groovy:groovy-all:2.2.1'
}
task( runSimple, dependsOn:'classes', type:JavaExec ) {
main = 'test.Main'
classpath = sourceSets.main.runtimeClasspath
}
ListDelegate.groovy
package test
class ListDelegate {
@Delegate List numbers = []
}
Main.groovy
package test
class Main {
static main( args ) {
def del = new ListDelegate()
del << 1
del << 'tim'
println del
}
}
Now, running gradle runSimple gives us the output:
:compileJava UP-TO-DATE
:compileGroovy
:processResources UP-TO-DATE
:classes
:runSimple
[1, tim]
BUILD SUCCESSFUL
Total time: 6.644 secs
So as you can see, the generics were thrown away, and it just worked adding Integers and Strings to out List of supposedly only Integers
Now, if we change ListDelegate.groovy to:
package test
import groovy.transform.*
@CompileStatic
class ListDelegate {
@Delegate List numbers = []
}
And run again:
:compileJava UP-TO-DATE
:compileGroovy
:processResources UP-TO-DATE
:classes
:runSimple
[1, tim]
BUILD SUCCESSFUL
Total time: 6.868 secs
We get the same output!! This is because whilst ListDelegate is now statically compiled, our Main class is still dynamic, so still throws away generics before constructing the ListDelegate... So we can also change Main.groovy to:
package test
import groovy.transform.*
@CompileStatic
class Main {
static main( args ) {
def del = new ListDelegate()
del << 1
del << 'tim'
println del
}
}
And now re-running gradle runSimple give us:
:compileJava UP-TO-DATE
:compileGroovy
startup failed:
/Users/tyates/Code/Groovy/generics/src/main/groovy/test/Main.groovy: 10:
[Static type checking] - Cannot find matching method test.ListDelegate#leftShift(java.lang.String).
Please check if the declared type is right and if the method exists.
@ line 10, column 9.
del << 'tim'
^
1 error
:compileGroovy FAILED
Which is, as you'd expect, failing to add a String to our declared List of Integer.
In fact, you only need to CompileStatic the Main.groovy class and this error will be picked up, but I always like to use it where I can, not just where I need to.