blob: bfcc72c200039aa36966482c7c72f34de11646e0 [file] [log] [blame]
//////////////////////////////////////////
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
//////////////////////////////////////////
= Semantics
This chapter covers the semantics of the Groovy programming language.
== Statements
=== Variable definition
Variables can be defined using either their type (like `String`) or by using the keyword `def`:
[source,groovy]
----
include::{projectdir}/src/spec/test/SemanticsTest.groovy[tags=variable_definition_example,indent=0]
----
`def` is a replacement for a type name. In variable definitions it is used to indicate that you don't care about the type. In variable definitions it is mandatory to either provide a type name explicitly or to use "def" in replacement. This is needed to make variable definitions detectable for the Groovy parser.
You can think of `def` as an alias of `Object` and you will understand it in an instant.
[NOTE]
Variable definition types can be refined by using generics, like in `List<String> names`.
To learn more about the generics support, please read the <<generics,generics section>>.
=== Variable assignment
You can assign values to variables for later use. Try the following:
[source,groovy]
----
include::{projectdir}/src/spec/test/SemanticsTest.groovy[tags=variable_assignment_example,indent=0]
----
==== Multiple assignment
Groovy supports multiple assignment, i.e. where multiple variables can be assigned at once, e.g.:
[source,groovy]
----
include::{projectdir}/src/spec/test/SemanticsTest.groovy[tags=multiple_assignment_example,indent=0]
----
You can provide types as part of the declaration if you wish:
[source,groovy]
----
include::{projectdir}/src/spec/test/SemanticsTest.groovy[tags=multiple_assignment_with_types,indent=0]
----
As well as used when declaring variables it also applies to existing variables:
[source,groovy]
----
include::{projectdir}/src/spec/test/SemanticsTest.groovy[tags=multiple_assignment_with_existing_variables,indent=0]
----
The syntax works for arrays as well as lists, as well as methods that return either of these:
[source,groovy]
----
include::{projectdir}/src/spec/test/SemanticsTest.groovy[tags=multiple_assignment_with_arrays_and_lists,indent=0]
----
==== Overflow and Underflow
If the left hand side has too many variables, excess ones are filled with null's:
[source,groovy]
----
include::{projectdir}/src/spec/test/SemanticsTest.groovy[tags=multiple_assignment_overflow,indent=0]
----
If the right hand side has too many variables, the extra ones are ignored:
[source,groovy]
----
include::{projectdir}/src/spec/test/SemanticsTest.groovy[tags=multiple_assignment_underflow,indent=0]
----
==== Object destructuring with multiple assignment
In the section describing the various <<groovy-operators,Groovy operators>>,
the case of the <<subscript-operator,subscript operator>> has been covered,
explaining how you can override the `getAt()`/`putAt()` method.
With this technique, we can combine multiple assignments and the subscript operator methods to implement _object destructuring_.
Consider the following immutable `Coordinates` class, containing a pair of longitude and latitude doubles,
and notice our implementation of the `getAt()` method:
[source,groovy]
----
include::{projectdir}/src/spec/test/SemanticsTest.groovy[tags=coordinates-class,indent=0]
----
Now let's instantiate this class and destructure its longitude and latitude:
[source,groovy]
----
include::{projectdir}/src/spec/test/SemanticsTest.groovy[tags=destructuring,indent=0]
----
<1> we create an instance of the `Coordinates` class
<2> then, we use a multiple assignment to get the individual longitude and latitude values
<3> and we can finally assert their values.
=== Control structures
==== Conditional structures
===== if / else
Groovy supports the usual if - else syntax from Java
[source,groovy]
----
include::{projectdir}/src/spec/test/SemanticsTest.groovy[tags=if_else_example,indent=0]
----
Groovy also supports the normal Java "nested" if then else if syntax:
[source,groovy]
----
if ( ... ) {
...
} else if (...) {
...
} else {
...
}
----
===== switch / case
The switch statement in Groovy is backwards compatible with Java code; so you can fall through cases sharing the same code for multiple matches.
One difference though is that the Groovy switch statement can handle any kind of switch value and different kinds of matching can be performed.
[source,groovy]
----
include::{projectdir}/src/spec/test/SemanticsTest.groovy[tags=switch_case_example,indent=0]
----
Switch supports the following kinds of comparisons:
* Class case values match if the switch value is an instance of the class
* Regular expression case values match if the `toString()` representation of the switch value matches the regex
* Collection case values match if the switch value is contained in the collection. This also includes ranges (since they are Lists)
* Closure case values match if the calling the closure returns a result which is true according to the <<Groovy-Truth,Groovy truth>>
* If none of the above are used then the case value matches if the case value equals the switch value
NOTE: When using a closure case value, the default `it` parameter is actually the switch value (in our example, variable `x`).
==== Looping structures
===== Classic for loop
Groovy supports the standard Java / C for loop:
[source,groovy]
----
include::{projectdir}/src/spec/test/SemanticsTest.groovy[tags=classic_for_loop_example,indent=0]
----
===== Enhanced classic Java-style for loop
The more elaborate form of Java's classic for loop with comma-separate expressions
is now supported. Example:
[source,groovy]
--------------------------------------
def facts = []
def count = 5
for (int fact = 1, i = 1; i <= count; i++, fact *= i) {
facts << fact
}
assert facts == [1, 2, 6, 24, 120]
--------------------------------------
===== Multi-assignment in combination with for loop
Groovy has supported multi-assignment statements since Groovy 1.6:
[source,groovy]
--------------------------------------
// multi-assignment with types
def (String x, int y) = ['foo', 42]
assert "$x $y" == 'foo 42'
--------------------------------------
These can now appear in for loops:
[source,groovy]
--------------------------------------
// multi-assignment goes loopy
def baNums = []
for (def (String u, int v) = ['bar', 42]; v < 45; u++, v++) {
baNums << "$u $v"
}
assert baNums == ['bar 42', 'bas 43', 'bat 44']
--------------------------------------
===== for in loop
The for loop in Groovy is much simpler and works with any kind of array, collection, Map, etc.
[source,groovy]
----
include::{projectdir}/src/spec/test/SemanticsTest.groovy[tags=groovy_for_loop_example,indent=0]
----
[NOTE]
Groovy also supports the Java colon variation with colons: `for (char c : text) {}`
===== while loop
Groovy supports the usual while {...} loops like Java:
[source,groovy]
----
include::{projectdir}/src/spec/test/SemanticsTest.groovy[tags=while_loop_example,indent=0]
----
===== do/while loop
Java's class do/while loop is now supported. Example:
[source,groovy]
--------------------------------------
// classic Java-style do..while loop
def count = 5
def fact = 1
do {
fact *= count--
} while(count > 1)
assert fact == 120
--------------------------------------
==== Exception handling
Exception handling is the same as Java.
==== try / catch / finally
You can specify a complete `try-catch-finally`, a `try-catch`, or a `try-finally` set of blocks.
NOTE: Braces are required around each block's body.
[source,groovy]
----
include::{projectdir}/src/spec/test/SemanticsTest.groovy[tags=try_catch_example,indent=0]
----
We can put code within a 'finally' clause following a matching 'try' clause, so that regardless of whether the code in the 'try' clause throws an exception, the code in the finally clause will always execute:
[source,groovy]
----
include::{projectdir}/src/spec/test/SemanticsTest.groovy[tags=try_catch_finally_example,indent=0]
----
==== Multi-catch
With the multi catch block (since Groovy 2.0), we're able to define several exceptions to be catch and treated by the same catch block:
[source,groovy]
----
try {
/* ... */
} catch ( IOException | NullPointerException e ) {
/* one block to handle 2 exceptions */
}
----
==== ARM Try with resources
Groovy often provides better alternatives to Java 7's `try`-with-resources statement for Automatic Resource Management (ARM).
That syntax is now supported for Java programmers migrating to Groovy and still wanting to use the old style:
[source,groovy]
--------------------------------------
class FromResource extends ByteArrayInputStream {
@Override
void close() throws IOException {
super.close()
println "FromResource closing"
}
FromResource(String input) {
super(input.toLowerCase().bytes)
}
}
class ToResource extends ByteArrayOutputStream {
@Override
void close() throws IOException {
super.close()
println "ToResource closing"
}
}
def wrestle(s) {
try (
FromResource from = new FromResource(s)
ToResource to = new ToResource()
) {
to << from
return to.toString()
}
}
def wrestle2(s) {
FromResource from = new FromResource(s)
try (from; ToResource to = new ToResource()) { // Enhanced try-with-resources in Java 9+
to << from
return to.toString()
}
}
assert wrestle("ARM was here!").contains('arm')
assert wrestle2("ARM was here!").contains('arm')
--------------------------------------
Which yields the following output:
--------------------------------------
ToResource closing
FromResource closing
ToResource closing
FromResource closing
--------------------------------------
=== Power assertion
Unlike Java with which Groovy shares the `assert` keyword, the latter in Groovy behaves very differently. First of all,
an assertion in Groovy is always executed, independently of the `-ea` flag of the JVM. It makes this a first class choice
for unit tests. The notion of "power asserts" is directly related to how the Groovy `assert` behaves.
A power assertion is decomposed into 3 parts:
----
assert [left expression] == [right expression] : (optional message)
----
The result of the assertion is very different from what you would get in Java. If the assertion is true, then nothing
happens. If the assertion is false, then it provides a visual representation of the value of each sub-expressions of the
expression being asserted. For example:
[source,groovy]
----
include::{projectdir}/src/spec/test/semantics/PowerAssertTest.groovy[tags=assert_code_1,indent=0]
----
Will yield:
----
Caught: Assertion failed:
include::{projectdir}/src/spec/test/semantics/PowerAssertTest.groovy[tags=assert_error_1,indent=0]
----
Power asserts become very interesting when the expressions are more complex, like in the next example:
[source,groovy]
----
include::{projectdir}/src/spec/test/semantics/PowerAssertTest.groovy[tags=assert_code_2,indent=0]
----
Which will print the value for each sub-expression:
[source,groovy]
----
include::{projectdir}/src/spec/test/semantics/PowerAssertTest.groovy[tags=assert_error_2,indent=0]
----
In case you don't want a pretty printed error message like above, you can fallback to a custom error message by
changing the optional message part of the assertion, like in this example:
[source,groovy]
----
include::{projectdir}/src/spec/test/semantics/PowerAssertTest.groovy[tags=assert_code_3,indent=0]
----
Which will print the following error message:
[source,groovy]
----
include::{projectdir}/src/spec/test/semantics/PowerAssertTest.groovy[tags=assert_error_3,indent=0]
----
=== Labeled statements
Any statement can be associated with a label. Labels do not impact the semantics of the code and can be used to make
the code easier to read like in the following example:
[source,groovy]
----
include::{projectdir}/src/spec/test/semantics/LabelsTest.groovy[tags=test_labels,indent=0]
----
Despite not changing the semantics of the labelled statement, it is possible to use labels in the `break` instruction
as a target for jump, as in the next example. However, even if this is allowed, this coding style is in general considered
a bad practice:
[source,groovy]
----
include::{projectdir}/src/spec/test/semantics/LabelsTest.groovy[tags=label_bad_practice,indent=0]
----
It is important to understand that by default labels have no impact on the semantics of the code, however they belong to the abstract
syntax tree (AST) so it is possible for an AST transformation to use that information to perform transformations over
the code, hence leading to different semantics. This is in particular what the http://spockframework.github.io/spock/docs/current/index.html[Spock Framework]
does to make testing easier.
== Expressions
(TBD)
[[gpath_expressions]]
=== GPath expressions
////
This is covered in {projectdir}/subprojects/groovy-xml/{specfolder}/xml-userguide.adoc, where the legacy codehaus GPath wiki page
have been converted.
Current section should explain what is an GPath expression (not just example, but more like a formal language specification, but kept simple).
////
`GPath` is a path expression language integrated into Groovy which allows parts of nested structured data to be identified. In this
sense, it has similar aims and scope as XPath does for XML. GPath is often used in the context of processing XML, but it really applies
to any object graph. Where XPath uses a filesystem-like path notation, a tree hierarchy with parts separated by a slash `/`, GPath *use a
dot-object notation* to perform object navigation.
As an example, you can specify a path to an object or element of interest:
* `a.b.c` -> for XML, yields all the `c` elements inside `b` inside `a`
* `a.b.c` -> for POJOs, yields the `c` properties for all the `b` properties of `a` (sort of like `a.getB().getC()` in JavaBeans)
In both cases, the GPath expression can be viewed as a query on an object graph. For POJOs, the object graph is most often built by the
program being written through object instantiation and composition; for XML processing, the object graph is the result of `parsing`
the XML text, most often with classes like XmlParser or XmlSlurper. See <<{projectdir}/subprojects/groovy-xml/{specfolder}/xml-userguide.adoc#Processing XML,Processing XML>>
for more in-depth details on consuming XML in Groovy.
[TIP]
====
When querying the object graph generated from XmlParser or XmlSlurper, a GPath expression can refer to attributes defined on elements with
the `@` notation:
* `a["@href"]` -> map-like notation : the href attribute of all the a elements
* `a.'@href'` -> property notation : an alternative way of expressing this
* `a.@href` -> direct notation : yet another alternative way of expressing this
====
==== Object navigation
Let's see an example of a GPath expression on a simple _object graph_, the one obtained using java reflection. Suppose you are in a non-static method of a
class having another method named `aMethodFoo`
[source,groovy]
----
include::{projectdir}/src/spec/test/semantics/GPathTest.groovy[tags=gpath_on_reflection_1,indent=0]
----
the following GPath expression will get the name of that method:
[source,groovy]
----
include::{projectdir}/src/spec/test/semantics/GPathTest.groovy[tags=gpath_on_reflection_2,indent=0]
----
_More precisely_, the above GPath expression produces a list of String, each being the name of an existing method on `this` where that name ends with `Foo`.
Now, given the following methods also defined in that class:
[source,groovy]
----
include::{projectdir}/src/spec/test/semantics/GPathTest.groovy[tags=gpath_on_reflection_3,indent=0]
----
then the following GPath expression will get the names of *(1)* and *(3)*, but not *(2)* or *(0)*:
[source,groovy]
----
include::{projectdir}/src/spec/test/semantics/GPathTest.groovy[tags=gpath_on_reflection_4,indent=0]
----
==== Expression Deconstruction
We can decompose the expression `this.class.methods.name.grep(~/.*Bar/)` to get an idea of how a GPath is evaluated:
`this.class`:: property accessor, equivalent to `this.getClass()` in Java, yields a `Class` object.
`this.class.methods`:: property accessor, equivalent to `this.getClass().getMethods()`, yields an array of `Method` objects.
`this.class.methods.name`:: apply a property accessor on each element of an array and produce a list of the results.
`this.class.methods.name.grep(...)`:: call method `grep` on each element of the list yielded by `this.class.methods.name` and produce a list of the results.
WARNING: A sub-expression like `this.class.methods` yields an array because this is what calling `this.getClass().getMethods()` in Java
would produce. `GPath` expressions do not have a convention where a `s` means a list or anything like that.
One powerful feature of GPath expression is that property access on a collection is converted to a _property access on each element of the collection_ with
the results collected into a collection. Therefore, the expression `this.class.methods.name` could be expressed as follows in Java:
[source,java]
----
List<String> methodNames = new ArrayList<String>();
for (Method method : this.getClass().getMethods()) {
methodNames.add(method.getName());
}
return methodNames;
----
Array access notation can also be used in a GPath expression where a collection is present :
[source,groovy]
----
include::{projectdir}/src/spec/test/semantics/GPathTest.groovy[tags=gpath_array_access_1,indent=0]
----
NOTE: array access are zero-based in GPath expressions
==== GPath for XML navigation
Here is an example with a XML document and various form of GPath expressions:
[source,groovy]
----
include::{projectdir}/src/spec/test/semantics/GPathTest.groovy[tags=gpath_on_xml_1,indent=0]
----
<1> There is one `level` node under `root`
<2> There are two `sublevel` nodes under `root/level`
<3> There is one element `sublevel` having an attribute `id` with value `1`
<4> Text value of `key` element of first `keyVal` element of second `sublevel` element under `root/level` is 'anotherKey'
== Promotion and coercion
=== Number promotion
The rules of number promotion are specified in the section on <<_math_operations,math operations>>.
[[closure-coercion]]
=== Closure to type coercion
==== Assigning a closure to a SAM type
A SAM type is a type which defines a single abstract method. This includes:
[source,groovy]
.Functional interfaces
----
include::{projectdir}/src/spec/test/CoercionTest.groovy[tags=filter_sam_type,indent=0]
----
[source,groovy]
.Abstract classes with single abstract method
----
include::{projectdir}/src/spec/test/CoercionTest.groovy[tags=greeter_sam_type,indent=0]
----
Any closure can be converted into a SAM type using the `as` operator:
[source,groovy]
----
include::{projectdir}/src/spec/test/CoercionTest.groovy[tags=assertions_explicit_closure_to_sam,indent=0]
----
However, the `as Type` expression is optional since Groovy 2.2.0. You can omit it and simply write:
[source,groovy]
----
include::{projectdir}/src/spec/test/CoercionTest.groovy[tags=assertions_implicit_closure_to_sam,indent=0]
----
which means you are also allowed to use method pointers, as shown in the following example:
[source,groovy]
----
include::{projectdir}/src/spec/test/CoercionTest.groovy[tags=assertions_implicit_closure_to_sam_and_method_pointer,indent=0]
----
==== Calling a method accepting a SAM type with a closure
The second and probably more important use case for closure to SAM type coercion is calling a method which accepts
a SAM type. Imagine the following method:
[source,groovy]
-----
include::{projectdir}/src/spec/test/CoercionTest.groovy[tags=method_accepting_filter,indent=0]
-----
Then you can call it with a closure, without having to create an explicit implementation of the interface:
[source,groovy]
-----
include::{projectdir}/src/spec/test/CoercionTest.groovy[tags=method_call_with_explicit_coercion,indent=0]
-----
But since Groovy 2.2.0, you are also able to omit the explicit coercion and call the method as if it used a closure:
[source,groovy]
-----
include::{projectdir}/src/spec/test/CoercionTest.groovy[tags=method_call_with_implicit_coercion,indent=0]
-----
As you can see, this has the advantage of letting you use the closure syntax for method calls, that is to say put the
closure outside of the parenthesis, improving the readability of your code.
==== Closure to arbitrary type coercion
In addition to SAM types, a closure can be coerced to any type and in particular interfaces. Let's define the
following interface:
[source,groovy]
----
include::{projectdir}/src/spec/test/CoercionTest.groovy[tags=foobar_interface,indent=0]
----
You can coerce a closure into the interface using the `as` keyword:
[source,groovy]
----
include::{projectdir}/src/spec/test/CoercionTest.groovy[tags=foobar2closure_coercion,indent=0]
----
This produces a class for which all methods are implemented using the closure:
[source,groovy]
----
include::{projectdir}/src/spec/test/CoercionTest.groovy[tags=foobarintf_assertions,indent=0]
----
But it is also possible to coerce a closure to any class. For example, we can replace the `interface` that we defined
with `class` without changing the assertions:
[source,groovy]
----
include::{projectdir}/src/spec/test/CoercionTest.groovy[tags=closure2foobarclass,indent=0]
----
=== Map to type coercion
Usually using a single closure to implement an interface or a class with multiple methods is not the way to go. As an
alternative, Groovy allows you to coerce a map into an interface or a class. In that case, keys of the map are
interpreted as method names, while the values are the method implementation. The following example illustrates the
coercion of a map into an `Iterator`:
[source,groovy]
----
include::{projectdir}/src/spec/test/CoercionTest.groovy[tags=coerce_map_to_iterator,indent=0]
----
Of course this is a rather contrived example, but illustrates the concept. You only need to implement those methods
that are actually called, but if a method is called that doesn't exist in the map a `MissingMethodException` or an
`UnsupportedOperationException` is thrown, depending on the arguments passed to the call,
as in the following example:
[source,groovy]
----
include::{projectdir}/src/spec/test/CoercionTest.groovy[tags=define_x_interface,indent=0]
include::{projectdir}/src/spec/test/CoercionTest.groovy[tags=call_existing_method,indent=0]
include::{projectdir}/src/spec/test/CoercionTest.groovy[tags=call_non_existing_method,indent=0]
include::{projectdir}/src/spec/test/CoercionTest.groovy[tags=call_notimplemented_method,indent=0]
----
The type of the exception depends on the call itself:
* `MissingMethodException` if the arguments of the call do not match those from the interface/class
* `UnsupportedOperationException` if the arguments of the call match one of the overloaded methods of the interface/class
=== String to enum coercion
Groovy allows transparent `String` (or `GString`) to enum values coercion. Imagine you define the following enum:
[source,groovy]
----
include::{projectdir}/src/spec/test/CoercionTest.groovy[tags=state_enum,indent=0]
----
then you can assign a string to the enum without having to use an explicit `as` coercion:
[source,groovy]
----
include::{projectdir}/src/spec/test/CoercionTest.groovy[tags=enum_coerce_assignment,indent=0]
----
It is also possible to use a `GString` as the value:
[source,groovy]
----
include::{projectdir}/src/spec/test/CoercionTest.groovy[tags=enum_coerce_assignment_gstring,indent=0]
----
However, this would throw a runtime error (`IllegalArgumentException`):
[source,groovy]
----
include::{projectdir}/src/spec/test/CoercionTest.groovy[tags=enum_coerce_assignment_wrong,indent=0]
----
Note that it is also possible to use implicit coercion in switch statements:
[source,groovy]
----
include::{projectdir}/src/spec/test/CoercionTest.groovy[tags=enum_switch_method,indent=0]
----
in particular, see how the `case` use string constants. But if you call a method that uses an enum with a `String`
argument, you still have to use an explicit `as` coercion:
[source,groovy]
----
include::{projectdir}/src/spec/test/CoercionTest.groovy[tags=enum_switch_test,indent=0]
----
=== Custom type coercion
It is possible for a class to define custom coercion strategies by implementing the `asType` method. Custom coercion
is invoked using the `as` operator and is never implicit. As an example,
imagine you defined two classes, `Polar` and `Cartesian`, like in the following example:
[source,groovy]
----
include::{projectdir}/src/spec/test/CoercionTest.groovy[tags=polar_class_header,indent=0]
include::{projectdir}/src/spec/test/CoercionTest.groovy[tags=polar_class_footer,indent=0]
include::{projectdir}/src/spec/test/CoercionTest.groovy[tags=cartesian_class,indent=0]
----
And that you want to convert from polar coordinates to cartesian coordinates. One way of doing this is to define
the `asType` method in the `Polar` class:
[source,groovy]
----
include::{projectdir}/src/spec/test/CoercionTest.groovy[tags=polar_class_astype,indent=0]
----
which allows you to use the `as` coercion operator:
[source,groovy]
----
include::{projectdir}/src/spec/test/CoercionTest.groovy[tags=polar_astype_assert,indent=0]
----
Putting it all together, the `Polar` class looks like this:
[source,groovy]
----
include::{projectdir}/src/spec/test/CoercionTest.groovy[tags=polar_class_header,indent=0]
include::{projectdir}/src/spec/test/CoercionTest.groovy[tags=polar_class_astype,indent=4]
include::{projectdir}/src/spec/test/CoercionTest.groovy[tags=polar_class_footer,indent=0]
----
but it is also possible to define `asType` outside of the `Polar` class, which can be practical if you want to define
custom coercion strategies for "closed" classes or classes for which you don't own the source code, for example using
a metaclass:
[source,groovy]
----
include::{projectdir}/src/spec/test/CoercionTest.groovy[tags=polar_metaclass_astype,indent=0]
----
=== Class literals vs variables and the as operator
Using the `as` keyword is only possible if you have a static reference to a class, like in the following code:
[source,groovy]
----
include::{projectdir}/src/spec/test/CoercionTest.groovy[tags=as_keyword,indent=0]
----
But what if you get the class by reflection, for example by calling `Class.forName`?
[source,groovy]
----
include::{projectdir}/src/spec/test/CoercionTest.groovy[tags=clazz_greeter_header,indent=0]
----
Trying to use the reference to the class with the `as` keyword would fail:
[source,groovy]
----
include::{projectdir}/src/spec/test/CoercionTest.groovy[tags=incorrect_as_usage,indent=0]
----
It is failing because the `as` keyword only works with class literals. Instead, you need to call the `asType` method:
[source,groovy]
----
include::{projectdir}/src/spec/test/CoercionTest.groovy[tags=fixed_as_usage,indent=0]
----
== Optionality
=== Optional parentheses
Method calls can omit the parentheses if there is at least one parameter and there is no ambiguity:
[source,groovy]
----
include::{projectdir}/src/spec/test/semantics/OptionalityTest.groovy[tags=optional_parentheses,indent=0]
----
Parentheses are required for method calls without parameters or ambiguous method calls:
[source,groovy]
----
include::{projectdir}/src/spec/test/semantics/OptionalityTest.groovy[tags=required_parentheses,indent=0]
----
=== Optional semicolons
In Groovy semicolons at the end of the line can be omitted, if the line contains only a single statement.
This means that:
[source,groovy]
----
include::{projectdir}/src/spec/test/semantics/OptionalityTest.groovy[tags=single_statement_with_semicolon,indent=0]
----
can be more idiomatically written as:
[source,groovy]
----
include::{projectdir}/src/spec/test/semantics/OptionalityTest.groovy[tags=single_statement_without_semicolon,indent=0]
----
Multiple statements in a line require semicolons to separate them:
[source,groovy]
----
include::{projectdir}/src/spec/test/semantics/OptionalityTest.groovy[tags=statements_separated_by_semicolon,indent=0]
----
=== Optional return keyword
In Groovy, the last expression evaluated in the body of a method or a closure is returned. This means that the `return` keyword is optional.
[source,groovy]
----
include::{projectdir}/src/spec/test/semantics/OptionalityTest.groovy[tags=return_keyword,indent=0]
----
Can be shortened to:
[source,groovy]
----
include::{projectdir}/src/spec/test/semantics/OptionalityTest.groovy[tags=omitted_return_keyword,indent=0]
----
=== Optional public keyword
By default, Groovy classes and methods are `public`. Therefore this class:
[source,groovy]
----
include::{projectdir}/src/spec/test/semantics/OptionalityTest.groovy[tags=public_keyword,indent=0]
----
is identical to this class:
[source,groovy]
----
include::{projectdir}/src/spec/test/semantics/OptionalityTest.groovy[tags=omitted_public,indent=0]
----
[[Groovy-Truth]]
== The Groovy Truth
Groovy decides whether a expression is true or false by applying the rules given below.
=== Boolean expressions
True if the corresponding Boolean value is `true`.
[source,groovy]
----
include::{projectdir}/src/spec/test/semantics/TheGroovyTruthTest.groovy[tags=boolean_truth,indent=0]
----
=== Collections and Arrays
Non-empty Collections and arrays are true.
[source,groovy]
----
include::{projectdir}/src/spec/test/semantics/TheGroovyTruthTest.groovy[tags=collection_truth,indent=0]
----
=== Matchers
True if the Matcher has at least one match.
[source,groovy]
----
include::{projectdir}/src/spec/test/semantics/TheGroovyTruthTest.groovy[tags=matcher_truth,indent=0]
----
=== Iterators and Enumerations
Iterators and Enumerations with further elements are coerced to true.
[source,groovy]
----
include::{projectdir}/src/spec/test/semantics/TheGroovyTruthTest.groovy[tags=iterator_enumeration_truth,indent=0]
----
=== Maps
Non-empty Maps are evaluated to true.
[source,groovy]
----
include::{projectdir}/src/spec/test/semantics/TheGroovyTruthTest.groovy[tags=map_truth,indent=0]
----
=== Strings
Non-empty Strings, GStrings and CharSequences are coerced to true.
[source,groovy]
----
include::{projectdir}/src/spec/test/semantics/TheGroovyTruthTest.groovy[tags=string_truth,indent=0]
----
=== Numbers
Non-zero numbers are true.
[source,groovy]
----
include::{projectdir}/src/spec/test/semantics/TheGroovyTruthTest.groovy[tags=number_truth,indent=0]
----
=== Object References
Non-null object references are coerced to true.
[source,groovy]
----
include::{projectdir}/src/spec/test/semantics/TheGroovyTruthTest.groovy[tags=object_truth,indent=0]
----
=== Customizing the truth with asBoolean() methods
In order to customize whether groovy evaluates your object to `true` or `false` implement the `asBoolean()` method:
[source,groovy]
----
include::{projectdir}/src/spec/test/semantics/TheGroovyTruthTest.groovy[tags=asBoolean_object,indent=0]
----
Groovy will call this method to coerce your object to a boolean value, e.g.:
[source,groovy]
----
include::{projectdir}/src/spec/test/semantics/TheGroovyTruthTest.groovy[tags=asBoolean_usage,indent=0]
----
== Typing
=== Optional typing
Optional typing is the idea that a program can work even if you don't put an explicit type on a variable. Being a dynamic
language, Groovy naturally implements that feature, for example when you declare a variable:
[source,groovy]
----
include::{projectdir}/src/spec/test/typing/OptionalTypingTest.groovy[tags=optionaltyping_var,indent=0]
----
<1> `foo` is declared using an explicit type, `String`
<2> we can call the `toUpperCase` method on a `String`
Groovy will let you write this instead:
[source,groovy]
----
include::{projectdir}/src/spec/test/typing/OptionalTypingTest.groovy[tags=optionaltyping_var_def,indent=0]
----
<1> `foo` is declared using `def`
<2> we can still call the `toUpperCase` method, because the type of `aString` is resolved at runtime
So it doesn't matter that you use an explicit type here. It is in particular interesting when you combine this feature
with <<static-type-checking,static type checking>>, because the type checker performs type inference.
Likewise, Groovy doesn't make it mandatory to declare the types of a parameter in a method:
[source,groovy]
----
include::{projectdir}/src/spec/test/typing/OptionalTypingTest.groovy[tags=optionaltyping_orig,indent=0]
----
can be rewritten using `def` as both return type and parameter types, in order to take advantage of duck typing, as
illustrated in this example:
[source,groovy]
----
include::{projectdir}/src/spec/test/typing/OptionalTypingTest.groovy[tags=optionaltyping_def,indent=0]
----
<1> both the return type and the parameter types use `def`
<2> it makes it possible to use the method with `String`
<3> but also with `int` since the `plus` method is defined
TIP: Using the `def` keyword here is recommended to describe the intent of a method which is supposed to work on any
type, but technically, we could use `Object` instead and the result would be the same: `def` is, in Groovy, strictly
equivalent to using `Object`.
Eventually, the type can be removed altogether from both the return type and the descriptor. But if you want to remove
it from the return type, you then need to add an explicit modifier for the method, so that the compiler can make a difference
between a method declaration and a method call, like illustrated in this example:
[source,groovy]
----
include::{projectdir}/src/spec/test/typing/OptionalTypingTest.groovy[tags=optionaltyping_notype,indent=0]
----
<1> if we want to omit the return type, an explicit modifier has to be set.
<2> it is still possible to use the method with `String`
<3> and also with `int`
TIP: Omitting types is in general considered a bad practice in method parameters or method return types for public APIs.
While using `def` in a local variable is not really a problem because the visibility of the variable is limited to the
method itself, while set on a method parameter, `def` will be converted to `Object` in the method signature, making it
difficult for users to know which is the expected type of the arguments. This means that you should limit this to cases
where you are explicitly relying on duck typing.
[[static-type-checking]]
=== Static type checking
By default, Groovy performs minimal type checking at compile time. Since it is primarily a dynamic language,
most checks that a static compiler would normally do aren't possible at compile time. A method added via runtime
metaprogramming might alter a class or object's runtime behavior. Let's illustrate why in the
following example:
[source,groovy]
----
include::{projectdir}/src/spec/test/typing/TypeCheckingTest.groovy[tags=stc_intro,indent=0]
----
<1> the `Person` class only defines two properties, `firstName` and `lastName`
<2> we can create an instance of Person
<3> and call a method named `formattedName`
It is quite common in dynamic languages for code such as the above example not to throw any error. How can this be?
In Java, this would typically fail at compile time. However, in Groovy, it will not fail at compile time, and if coded
correctly, will also not fail at runtime. In fact, to make this work at runtime, *one* possibility is to rely on
runtime metaprogramming. So just adding this line after the declaration of the `Person` class is enough:
[source,groovy]
----
include::{projectdir}/src/spec/test/typing/TypeCheckingTest.groovy[tags=stc_intro_magic,indent=0]
----
This means that in general, in Groovy, you can't make any assumption about the type of an object beyond its declaration
type, and even if you know it, you can't determine at compile time what method will be called, or which property will
be retrieved. It has a lot of interest, going from writing DSLs to testing, which is discussed in other sections of this
manual.
However, if your program doesn't rely on dynamic features and that you come from the static world (in particular, from
a Java mindset), not catching such "errors" at compile time can be surprising. As we have seen in the previous example,
the compiler cannot be sure this is an error. To make it aware that it is, you have to explicitly instruct the compiler
that you are switching to a type checked mode. This can be done by annotating a class or a method with `@groovy.transform.TypeChecked`.
When type checking is activated, the compiler performs much more work:
* type inference is activated, meaning that even if you use `def` on a local variable for example, the type checker will be
able to infer the type of the variable from the assignments
* method calls are resolved at compile time, meaning that if a method is not declared on a class, the compiler will throw an error
* in general, all the compile time errors that you are used to find in a static language will appear: method not found, property not found,
incompatible types for method calls, number precision errors, ...
In this section, we will describe the behavior of the type checker in various situations and explain the limits of using
`@TypeChecked` on your code.
==== The `@TypeChecked` annotation
===== Activating type checking at compile time
The `groovy.transform.TypeChecked` annotation enables type checking. It can be placed on a class:
[source,groovy]
----
include::{projectdir}/src/spec/test/typing/TypeCheckingTest.groovy[tags=typechecked_class,indent=0]
----
Or on a method:
[source,groovy]
----
include::{projectdir}/src/spec/test/typing/TypeCheckingTest.groovy[tags=typechecked_method,indent=0]
----
In the first case, all methods, properties, fields, inner classes, ... of the annotated class will be type checked, whereas
in the second case, only the method and potential closures or anonymous inner classes that it contains will be type checked.
===== Skipping sections
The scope of type checking can be restricted. For example, if a class is type checked, you can instruct the type checker
to skip a method by annotating it with `@TypeChecked(TypeCheckingMode.SKIP)`:
[source,groovy]
----
include::{projectdir}/src/spec/test/typing/TypeCheckingTest.groovy[tags=stc_skip,indent=0]
----
<1> the `GreetingService` class is marked as type checked
<2> so the `greeting` method is automatically type checked
<3> but `doGreet` is marked with `SKIP`
<4> the type checker doesn't complain about missing properties here
In the previous example, `SentenceBuilder` relies on dynamic code. There's no real `Hello` method or property, so the
type checker would normally complain and compilation would fail. Since the method that uses the builder is marked with
`TypeCheckingMode.SKIP`, type checking is _skipped_ for this method, so the code will compile, even if the rest of the
class is type checked.
The following sections describe the semantics of type checking in Groovy.
==== Type checking assignments
An object `o` of type `A` can be assigned to a variable of type `T` if and only if:
* `T` equals `A`
+
[.result]
====
[source,groovy]
----
include::{projectdir}/src/spec/test/typing/TypeCheckingTest.groovy[tags=stc_assign_equals,indent=0]
----
====
* _or_ `T` is one of `String`, `boolean`, `Boolean` or `Class`
+
[.result]
====
[source,groovy]
----
include::{projectdir}/src/spec/test/typing/TypeCheckingTest.groovy[tags=stc_assign_specialcase,indent=0]
----
====
* _or_ `o` is null and `T` is not a primitive type
+
[.result]
====
[source,groovy]
----
include::{projectdir}/src/spec/test/typing/TypeCheckingTest.groovy[tags=stc_assign_null,indent=0]
include::{projectdir}/src/spec/test/typing/TypeCheckingTest.groovy[tags=stc_assign_null2prim,indent=0]
----
====
* _or_ `T` is an array and `A` is an array and the component type of `A` is assignable to the component type of `T`
+
[.result]
====
[source,groovy]
----
include::{projectdir}/src/spec/test/typing/TypeCheckingTest.groovy[tags=stc_assign_array,indent=0]
include::{projectdir}/src/spec/test/typing/TypeCheckingTest.groovy[tags=stc_assign_array_fail,indent=0]
----
====
* _or_ `T` is an array and `A` is a list and the component type of `A` is assignable to the component type of `T`
+
[.result]
====
[source,groovy]
----
include::{projectdir}/src/spec/test/typing/TypeCheckingTest.groovy[tags=stc_assign_array_list,indent=0]
include::{projectdir}/src/spec/test/typing/TypeCheckingTest.groovy[tags=stc_assign_array_list_fail,indent=0]
----
====
* _or_ `T` is a superclass of `A`
+
[.result]
====
[source,groovy]
----
include::{projectdir}/src/spec/test/typing/TypeCheckingTest.groovy[tags=stc_assign_superclass,indent=0]
include::{projectdir}/src/spec/test/typing/TypeCheckingTest.groovy[tags=stc_assign_superclass_fail,indent=0]
----
====
* _or_ `T` is an interface implemented by `A`
+
[.result]
====
[source,groovy]
----
include::{projectdir}/src/spec/test/typing/TypeCheckingTest.groovy[tags=stc_assign_interface,indent=0]
include::{projectdir}/src/spec/test/typing/TypeCheckingTest.groovy[tags=stc_assign_interface_fail,indent=0]
----
====
* _or_ `T` or `A` are a primitive type and their boxed types are assignable
+
[.result]
====
[source,groovy]
----
include::{projectdir}/src/spec/test/typing/TypeCheckingTest.groovy[tags=stc_assign_prim,indent=0]
----
====
* _or_ `T` extends `groovy.lang.Closure` and `A` is a SAM-type (single abstract method type)
+
[.result]
====
[source,groovy]
----
include::{projectdir}/src/spec/test/typing/TypeCheckingTest.groovy[tags=stc_closure_coercion,indent=0]
----
====
* _or_ `T` and `A` derive from `java.lang.Number` and conform to the following table
[cols="1,1,2a"]
.Number types (java.lang.XXX)
[[number-assignment]]
|===
|T |A |Examples
|Double
|Any but BigDecimal or BigInteger
| [source,groovy]
----
include::{projectdir}/src/spec/test/typing/TypeCheckingTest.groovy[tags=stc_assign_to_double,indent=0]
----
|Float
|Any type but BigDecimal, BigInteger or Double
| [source,groovy]
----
include::{projectdir}/src/spec/test/typing/TypeCheckingTest.groovy[tags=stc_assign_to_float,indent=0]
----
|Long
|Any type but BigDecimal, BigInteger, Double or Float
| [source,groovy]
----
include::{projectdir}/src/spec/test/typing/TypeCheckingTest.groovy[tags=stc_assign_to_long,indent=0]
----
|Integer
|Any type but BigDecimal, BigInteger, Double, Float or Long
| [source,groovy]
----
include::{projectdir}/src/spec/test/typing/TypeCheckingTest.groovy[tags=stc_assign_to_int,indent=0]
----
|Short
|Any type but BigDecimal, BigInteger, Double, Float, Long or Integer
| [source,groovy]
----
include::{projectdir}/src/spec/test/typing/TypeCheckingTest.groovy[tags=stc_assign_to_short,indent=0]
----
|Byte
|Byte
| [source,groovy]
----
include::{projectdir}/src/spec/test/typing/TypeCheckingTest.groovy[tags=stc_assign_to_byte,indent=0]
----
|===
==== List and map constructors
In addition to the assignment rules above, if an assignment is deemed invalid, in type checked mode, a _list_ literal or a _map_ literal `A` can be assigned
to a variable of type `T` if:
* the assignment is a variable declaration and `A` is a list literal and `T` has a constructor whose parameters match the types of the elements in the list literal
* the assignment is a variable declaration and `A` is a map literal and `T` has a no-arg constructor and a property for each of the map keys
For example, instead of writing:
[source,groovy]
----
include::{projectdir}/src/spec/test/typing/TypeCheckingTest.groovy[tags=stc_ctor_point_classic,indent=0]
----
You can use a "list constructor":
[source,groovy]
----
include::{projectdir}/src/spec/test/typing/TypeCheckingTest.groovy[tags=stc_ctor_point_list,indent=0]
----
or a "map constructor":
[source,groovy]
----
include::{projectdir}/src/spec/test/typing/TypeCheckingTest.groovy[tags=stc_ctor_point_map,indent=0]
----
If you use a map constructor, additional checks are done on the keys of the map to check if a property of the same name
is defined. For example, the following will fail at compile time:
[source,groovy]
----
include::{projectdir}/src/spec/test/typing/TypeCheckingTest.groovy[tags=stc_ctor_fail,indent=0]
----
<1> The type checker will throw an error `No such property: age for class: Person` at compile time
==== Method resolution
In type checked mode, methods are resolved at compile time. Resolution works by name and arguments. The return type is
irrelevant to method selection. Types of arguments are matched against the types of the parameters following those rules:
An argument `o` of type `A` can be used for a parameter of type `T` if and only if:
* `T` equals `A`
+
[.result]
====
[source,groovy]
----
include::{projectdir}/src/spec/test/typing/TypeCheckingTest.groovy[tags=stc_argparam_equals,indent=0]
----
====
* _or_ `T` is a `String` and `A` is a `GString`
+
[.result]
====
[source,groovy]
----
include::{projectdir}/src/spec/test/typing/TypeCheckingTest.groovy[tags=stc_argparam_specialcase,indent=0]
----
====
* _or_ `o` is null and `T` is not a primitive type
+
[.result]
====
[source,groovy]
----
include::{projectdir}/src/spec/test/typing/TypeCheckingTest.groovy[tags=stc_argparam_null,indent=0]
include::{projectdir}/src/spec/test/typing/TypeCheckingTest.groovy[tags=stc_argparam_null2prim,indent=0]
----
====
* _or_ `T` is an array and `A` is an array and the component type of `A` is assignable to the component type of `T`
+
[.result]
====
[source,groovy]
----
include::{projectdir}/src/spec/test/typing/TypeCheckingTest.groovy[tags=stc_argparam_array,indent=0]
include::{projectdir}/src/spec/test/typing/TypeCheckingTest.groovy[tags=stc_argparam_array_fail,indent=0]
----
====
* _or_ `T` is a superclass of `A`
+
[.result]
====
[source,groovy]
----
include::{projectdir}/src/spec/test/typing/TypeCheckingTest.groovy[tags=stc_argparam_superclass,indent=0]
include::{projectdir}/src/spec/test/typing/TypeCheckingTest.groovy[tags=stc_argparam_superclass_fail,indent=0]
----
====
* _or_ `T` is an interface implemented by `A`
+
[.result]
====
[source,groovy]
----
include::{projectdir}/src/spec/test/typing/TypeCheckingTest.groovy[tags=stc_argparam_interface,indent=0]
include::{projectdir}/src/spec/test/typing/TypeCheckingTest.groovy[tags=stc_argparam_interface_fail,indent=0]
----
====
* _or_ `T` or `A` are a primitive type and their boxed types are assignable
+
[.result]
====
[source,groovy]
----
include::{projectdir}/src/spec/test/typing/TypeCheckingTest.groovy[tags=stc_argparam_prim,indent=0]
----
====
* _or_ `T` extends `groovy.lang.Closure` and `A` is a SAM-type (single abstract method type)
+
[.result]
====
[source,groovy]
----
include::{projectdir}/src/spec/test/typing/TypeCheckingTest.groovy[tags=stc_arg_closure_coercion,indent=0]
----
====
* _or_ `T` and `A` derive from `java.lang.Number` and conform to the same rules as <<number-assignment,assignment of numbers>>
If a method with the appropriate name and arguments is not found at compile time, an error is thrown. The difference with "normal" Groovy is
illustrated in the following example:
[source,groovy]
----
include::{projectdir}/src/spec/test/typing/TypeCheckingTest.groovy[tags=method_not_type_checked,indent=0]
----
<1> `printLine` is an error, but since we're in a dynamic mode, the error is not caught at compile time
The example above shows a class that Groovy will be able to compile. However, if you try to create an instance of `MyService` and call the
`doSomething` method, then it will fail *at runtime*, because `printLine` doesn't exist. Of course, we already showed how Groovy could make
this a perfectly valid call, for example by catching `MethodMissingException` or implementing a custom meta-class, but if you know you're
not in such a case, `@TypeChecked` comes handy:
[source,groovy]
----
include::{projectdir}/src/spec/test/typing/TypeCheckingTest.groovy[tags=method_type_checked,indent=0]
----
<1> `printLine` is this time a compile-time error
Just adding `@TypeChecked` will trigger compile time method resolution. The type checker will try to find a method `printLine` accepting
a `String` on the `MyService` class, but cannot find one. It will fail compilation with the following message:
`Cannot find matching method MyService#printLine(java.lang.String)`
====
IMPORTANT: It is important to understand the logic behind the type checker: it is a compile-time check, so by definition, the type checker
is not aware of any kind of *runtime* metaprogramming that you do. This means that code which is perfectly valid without `@TypeChecked` will
*not* compile anymore if you activate type checking. This is in particular true if you think of duck typing: +
[source,groovy]
----
include::{projectdir}/src/spec/test/typing/TypeCheckingTest.groovy[tags=ducktyping_failure,indent=0]
----
<1> we define a `Duck` class which defines a `quack` method
<2> we define another `QuackingBird` class which also defines a `quack` method
<3> `quacker` is loosely typed, so since the method is `@TypeChecked`, we will obtain a compile-time error
<4> even if in non type-checked Groovy, this would have passed
There are possible workarounds, like introducing an interface, but basically, by activating type checking, you gain type safety
but you loose some features of the language. Hopefully, Groovy introduces some features like flow typing to reduce the gap between
type-checked and non type-checked Groovy.
====
[[type-inference]]
==== Type inference
===== Principles
When code is annotated with `@TypeChecked`, the compiler performs type inference. It doesn't simply rely on static types, but also uses various
techniques to infer the types of variables, return types, literals, ... so that the code remains as clean as possible even if you activate the
type checker.
The simplest example is inferring the type of a variable:
[source,groovy]
----
include::{projectdir}/src/spec/test/typing/TypeCheckingTest.groovy[tags=simple_var_type_inference,indent=0]
include::{projectdir}/src/spec/test/typing/TypeCheckingTest.groovy[tags=simple_var_type_inference_fail,indent=0]
----
<1> a variable is declared using the `def` keyword
<2> calling `toUpperCase` is allowed by the type checker
<3> calling `upper` will fail at compile time
The reason the call to `toUpperCase` works is because the type of `message` was _inferred_ as being a `String`.
===== Variables vs fields in type inference
It is worth noting that although the compiler performs type inference on local variables, it does *not* perform any kind
of type inference on fields, always falling back to the *declared type* of a field. To illustrate this, let's take a
look at this example:
[source,groovy]
----
include::{projectdir}/src/spec/test/typing/TypeCheckingTest.groovy[tags=typeinference_field_vs_local_variable,indent=0]
----
<1> `someUntypedField` uses `def` as a declaration type
<2> `someTypedField` uses `String` as a declaration type
<3> we can assign *anything* to `someUntypedField`
<4> yet calling `toUpperCase` fails at compile time because the field is not typed properly
<5> we can assign a `String` to a field of type `String`
<6> and this time `toUpperCase` is allowed
<7> if we assign a `String` to a local variable
<8> then calling `toUpperCase` is allowed on the local variable
Why such a difference? The reason is _thread safety_. At compile time, we can't make *any* guarantee about the type of
a field. Any thread can access any field at any time and between the moment a field is assigned a variable of some
type in a method and the time is is used the line after, another thread may have changed the contents of the field. This
is not the case for local variables: we know if they "escape" or not, so we can make sure that the type of a variable is
constant (or not) over time. Note that even if a field is final, the JVM makes no guarantee about it, so the type checker
doesn't behave differently if a field is final or not.
TIP: This is one of the reasons why we recommend to use *typed* fields. While using `def` for local variables is perfectly
fine thanks to type inference, this is not the case for fields, which also belong to the public API of a class, hence the
type is important.
===== Collection literal type inference
Groovy provides a syntax for various type literals. There are three native collection literals in Groovy:
* lists, using the `[]` literal
* maps, using the `[:]` literal
* ranges, using `from..to` (inclusive) and `from..<to` (exclusive)
The inferred type of a literal depends on the elements of the literal, as illustrated in the following table:
[cols="2a,3a"]
|===
|Literal| Inferred type
|[source,groovy]
----
include::{projectdir}/src/spec/test/typing/TypeCheckingTest.groovy[tags=empty_list_literal_inference,indent=0]
----
|`java.util.List`
|[source,groovy]
----
include::{projectdir}/src/spec/test/typing/TypeCheckingTest.groovy[tags=list_literal_inference_simple,indent=0]
----
|`java.util.List<String>`
|[source,groovy]
----
include::{projectdir}/src/spec/test/typing/TypeCheckingTest.groovy[tags=list_literal_inference_gstring,indent=0]
----
|`java.util.List<GString>` be careful, a `GString` is *not* a `String`!
|[source,groovy]
----
include::{projectdir}/src/spec/test/typing/TypeCheckingTest.groovy[tags=empty_map_literal_inference,indent=0]
----
|`java.util.LinkedHashMap`
|[source,groovy]
----
include::{projectdir}/src/spec/test/typing/TypeCheckingTest.groovy[tags=map_literal_inference_simple,indent=0]
----
|`java.util.LinkedHashMap<String,String>`
|[source,groovy]
----
include::{projectdir}/src/spec/test/typing/TypeCheckingTest.groovy[tags=map_literal_inference_gstring,indent=0]
----
|`java.util.LinkedHashMap<GString,String>` be careful, the key is a `GString`!
|[source,groovy]
----
include::{projectdir}/src/spec/test/typing/TypeCheckingTest.groovy[tags=intRange_literal_inference,indent=0]
----
|`groovy.lang.IntRange`
|[source,groovy]
----
include::{projectdir}/src/spec/test/typing/TypeCheckingTest.groovy[tags=charRange_literal_inference,indent=0]
----
|`groovy.lang.Range<String>` : uses the type of the bounds to infer the component type of the range
|===
As you can see, with the noticeable exception of the `IntRange`, the inferred type makes use of generics types to describe
the contents of a collection. In case the collection contains elements of different types, the type checker still performs
type inference of the components, but uses the notion of <<section-lub,least upper bound>>.
[[section-lub]]
===== Least upper bound
In Groovy, the _least upper bound_ of two types `A` and `B` is defined as a type which:
* superclass corresponds to the common super class of `A` and `B`
* interfaces correspond to the interfaces implemented by both `A` and `B`
* if `A` or `B` is a primitive type and that `A` isn't equal to `B`, the least upper bound of `A` and `B` is the least
upper bound of their wrapper types
If `A` and `B` only have one (1) interface in common and that their common superclass is `Object`, then the LUB of both
is the common interface.
The least upper bound represents the minimal type to which both `A` and `B` can be assigned. So for example, if `A` and `B`
are both `String`, then the LUB (least upper bound) of both is also `String`.
[source,groovy]
----
include::{projectdir}/src/spec/test/typing/TypeCheckingTest.groovy[tags=least_upper_bound_simple,indent=0]
----
<1> the LUB of `String` and `String` is `String`
<2> the LUB of `ArrayList` and `LinkedList` is their common super type, `AbstractList`
<3> the LUB of `ArrayList` and `List` is their only common interface, `List`
<4> the LUB of two identical interfaces is the interface itself
<5> the LUB of `Bottom1` and `Bottom2` is their superclass `Top`
<6> the LUB of two types which have nothing in common is `Object`
In those examples, the LUB is always representable as a normal, JVM supported, type. But Groovy internally represents the LUB
as a type which can be more complex, and that you wouldn't be able to use to define a variable for example. To illustrate this,
let's continue with this example:
[source,groovy]
----
include::{projectdir}/src/spec/test/typing/TypeCheckingTest.groovy[tags=least_upper_bound_complex,indent=0]
----
What is the least upper bound of `Bottom` and `SerializableFooImpl`? They don't have a common super class (apart from `Object`),
but they do share 2 interfaces (`Serializable` and `Foo`), so their least upper bound is a type which represents the union of
two interfaces (`Serializable` and `Foo`). This type cannot be defined in the source code, yet Groovy knows about it.
In the context of collection type inference (and generic type inference in general), this becomes handy, because the type of the
components is inferred as the least upper bound. We can illustrate why this is important in the following example:
[source,groovy]
----
include::{projectdir}/src/spec/test/typing/TypeCheckingTest.groovy[tags=least_upper_bound_collection_inference,indent=0]
----
<1> the `Greeter` interface defines a single method, `greet`
<2> the `Salute` interface defines a single method, `salute`
<3> class `A` implements both `Greeter` and `Salute` but there's no explicit interface extending both
<4> same for `B`
<5> but `B` defines an additional `exit` method
<6> the type of `list` is inferred as "list of the LUB of `A` and `B`"
<7> so it is possible to call `greet` which is defined on both `A` and `B` through the `Greeter` interface
<8> and it is possible to call `salute` which is defined on both `A` and `B` through the `Salute` interface
<9> yet calling `exit` is a compile time error because it doesn't belong to the LUB of `A` and `B` (only defined in `B`)
The error message will look like:
----
[Static type checking] - Cannot find matching method Greeter or Salute#exit()
----
which indicates that the `exit` method is neither defines on `Greeter` nor `Salute`, which are the two interfaces defined
in the least upper bound of `A` and `B`.
===== instanceof inference
In normal, non type checked, Groovy, you can write things like:
[source,groovy]
----
include::{projectdir}/src/spec/test/typing/TypeCheckingTest.groovy[tags=instanceof_inference,indent=0]
----
<1> guard the method call with an `instanceof` check
<2> make the call
The method call works because of dynamic dispatch (the method is selected at runtime). The equivalent code in Java would
require to cast `o` to a `Greeter` before calling the `greeting` method, because methods are selected at compile time:
[source,groovy]
----
include::{projectdir}/src/spec/test/typing/TypeCheckingTest.groovy[tags=instanceof_java_equiv,indent=0]
----
However, in Groovy, even if you add `@TypeChecked` (and thus activate type checking) on the `doSomething` method, the
cast is *not* necessary. The compiler embeds _instanceof_ inference that makes the cast optional.
[[section-flow-typing]]
===== Flow typing
Flow typing is an important concept of Groovy in type checked mode and an extension of type inference. The idea is that
the compiler is capable of inferring the type of variables in the flow of the code, not just at initialization:
[source,groovy]
----
include::{projectdir}/src/spec/test/typing/TypeCheckingTest.groovy[tags=flowtyping_basics,indent=0]
----
<1> first, `o` is declared using `def` and assigned a `String`
<2> the compiler inferred that `o` is a `String`, so calling `toUpperCase` is allowed
<3> `o` is reassigned with a `double`
<4> calling `Math.sqrt` passes compilation because the compiler knows that at this point, `o` is a `double`
So the type checker is _aware_ of the fact that the concrete type of a variable is different over time. In particular,
if you replace the last assignment with:
[source,groovy]
----
include::{projectdir}/src/spec/test/typing/TypeCheckingTest.groovy[tags=flowtyping_basics_fail,indent=0]
----
The type checker will now fail at compile time, because it knows that `o` is a `double` when `toUpperCase` is called,
so it's a type error.
It is important to understand that it is not the fact of declaring a variable with `def` that triggers type inference.
Flow typing works for *any* variable of any type. Declaring a variable with an explicit type only constrains what you
can assign to the variable:
[source,groovy]
----
include::{projectdir}/src/spec/test/typing/TypeCheckingTest.groovy[tags=flowtyping_typeconstraints,indent=0]
----
<1> `list` is declared as an unchecked `List` and assigned a list literal of `String`s
<2> this line passes compilation because of flow typing: the type checker knows that `list` is at this point a `List<String>`
<3> but you can't assign a `String` to a `List` so this is a type checking error
You can also note that even if the variable is declared *without* generics information, the type checker knows what is
the component type. Therefore, such code would fail compilation:
[source,groovy]
----
include::{projectdir}/src/spec/test/typing/TypeCheckingTest.groovy[tags=flowtyping_typeconstraints_failure,indent=0]
----
<1> `list` is inferred as `List<String>`
<2> so adding an `int` to a `List<String>` is a compile-time error
Fixing this requires adding an explicit generic type to the declaration:
[source,groovy]
----
include::{projectdir}/src/spec/test/typing/TypeCheckingTest.groovy[tags=flowtyping_typeconstraints_fixed,indent=0]
----
<1> `list` declared as `List<? extends Serializable>` and initialized with an empty list
<2> elements added to the list conform to the declaration type of the list
<3> so adding an `int` to a `List<? extends Serializable>` is allowed
Flow typing has been introduced to reduce the difference in semantics between classic and static Groovy. In particular,
consider the behavior of this code in Java:
[source,java]
----
include::{projectdir}/src/spec/test/typing/TypeCheckingJavaTest.java[tags=java_method_selection_head,indent=0]
include::{projectdir}/src/spec/test/typing/TypeCheckingJavaTest.java[tags=java_method_selection_body,indent=0]
----
<1> `o` is declared as an `Object` and assigned a `String`
<2> we call the `compute` method with `o`
<3> and print the result
In Java, this code will output `Nope`, because method selection is done at compile time and based on the *declared* types.
So even if `o` is a `String` at runtime, it is still the `Object` version which is called, because `o` has been declared
as an `Object`. To be short, in Java, declared types are most important, be it variable types, parameter types or return
types.
In Groovy, we could write:
[source,groovy]
----
include::{projectdir}/src/spec/test/typing/TypeCheckingTest.groovy[tags=groovy_method_selection,indent=0]
----
But this time, it will return `6`, because the method which is chosen is chosen *at runtime*, based on the _actual_
argument types. So at runtime, `o` is a `String` so the `String` variant is used. Note that this behavior has nothing
to do with type checking, it's the way Groovy works in general: dynamic dispatch.
In type checked Groovy, we want to make sure the type checker selects the same method *at compile time*, that the runtime
would choose. It is not possible in general, due to the semantics of the language, but we can make things better with flow
typing. With flow typing, `o` is _inferred_ as a `String` when the `compute` method is called, so the version which takes
a `String` and returns an `int` is chosen. This means that we can infer the return type of the method to be an `int`, and
not a `String`. This is important for subsequent calls and type safety.
So in type checked Groovy, flow typing is a very important concept, which also implies that if `@TypeChecked` is applied,
methods are selected based on the _inferred types_ of the arguments, not on the declared types. This doesn't ensure 100%
type safety, because the type checker _may_ select a wrong method, but it ensures the closest semantics to dynamic Groovy.
===== Advanced type inference
A combination of <<section-flow-typing,flow typing>> and <<section-lub,least upper bound inference>> is used to perform
advanced type inference and ensure type safety in multiple situations. In particular, program control structures are
likely to alter the inferred type of a variable:
[source,groovy]
----
include::{projectdir}/src/spec/test/typing/TypeCheckingTest.groovy[tags=flow_lub_ifelse_header,indent=0]
include::{projectdir}/src/spec/test/typing/TypeCheckingTest.groovy[tags=flow_lub_ifelse_test,indent=0]
----
<1> if `someCondition` is true, `o` is assigned a `Top`
<2> if `someCondition` is false, `o` is assigned a `Bottom`
<3> calling `methodFromTop` is safe
<4> but calling `methodFromBottom` is not, so it's a compile time error
When the type checker visits an `if/else` control structure, it checks all variables which are assigned in `if/else` branches
and computes the <<section-lub,least upper bound>> of all assignments. This type is the type of the inferred variable
after the `if/else` block, so in this example, `o` is assigned a `Top` in the `if` branch and a `Bottom` in the `else`
branch. The <<section-lub,LUB>> of those is a `Top`, so after the conditional branches, the compiler infers `o` as being
a `Top`. Calling `methodFromTop` will therefore be allowed, but not `methodFromBottom`.
The same reasoning exists with closures and in particular closure shared variables. A closure shared variable is a variable
which is defined outside of a closure, but used inside a closure, as in this example:
[source,groovy]
----
include::{projectdir}/src/spec/test/typing/TypeCheckingTest.groovy[tags=closure_shared_variable_definition,indent=0]
----
<1> a variable named `text` is declared
<2> `text` is used from inside a closure. It is a _closure shared variable_.
Groovy allows developers to use those variables without requiring them to be final. This means that a closure shared
variable can be reassigned inside a closure:
[source,groovy]
----
include::{projectdir}/src/spec/test/typing/TypeCheckingTest.groovy[tags=closure_shared_variable_ex1,indent=0]
----
The problem is that a closure is an independent block of code that can be executed (or not) at *any* time. In particular,
`doSomething` may be asynchronous, for example. This means that the body of a closure doesn't belong to the main control
flow. For that reason, the type checker also computes, for each closure shared variable, the <<section-lub,LUB>> of all
assignments of the variable, and will use that `LUB` as the inferred type outside of the scope of the closure, like in
this example:
[source,groovy]
----
include::{projectdir}/src/spec/test/typing/TypeCheckingTest.groovy[tags=closure_shared_variable_ex2,indent=0]
----
<1> a closure-shared variable is first assigned a `Top`
<2> inside the closure, it is assigned a `Bottom`
<3> `methodFromTop` is allowed
<4> `methodFromBottom` is a compilation error
Here, it is clear that when `methodFromBottom` is called, there's no guarantee, at compile-time or runtime that the
type of `o` will _effectively_ be a `Bottom`. There are chances that it will be, but we can't make sure, because it's
asynchronous. So the type checker will only allow calls on the <<section-lub,least upper bound>>, which is here a `Top`.
==== Closures and type inference
The type checker performs special inference on closures, resulting on additional checks on one side and improved fluency
on the other side.
===== Return type inference
The first thing that the type checker is capable of doing is inferring the _return type_ of a closure. This is simply
illustrated in the following example:
[source,groovy]
----
include::{projectdir}/src/spec/test/typing/TypeCheckingTest.groovy[tags=closure_return_type_inf,indent=0]
----
<1> a closure is defined, and it returns a string (more precisely a `GString`)
<2> we call the closure and assign the result to a variable
<3> the type checker inferred that the closure would return a string, so calling `length()` is allowed
As you can see, unlike a method which declares its return type explicitly, there's no need to declare the return type
of a closure: its type is inferred from the body of the closure.
.Closures vs methods
****
It's worth noting that return type inference is only applicable to closures. While the type checker could do the
same on a method, it is in practice not desirable: _in general_, methods can be overridden and it is not statically
possible to make sure that the method which is called is not an overridden version. So flow typing would actually
think that a method returns something, while in reality, it could return something else, like illustrated in the
following example:
[source,groovy]
----
include::{projectdir}/src/spec/test/typing/TypeCheckingTest.groovy[tags=method_return_type_matters,indent=0]
----
<1> class `A` defines a method `compute` which effectively returns a `String`
<2> this will fail compilation because the return type of `compute` is `def`(aka `Object`)
<3> class `B` extends `A` and redefines `compute`, this type returning an `int`
As you can see, if the type checker relied on the inferred return type of a method, with <<section-flow-typing,flow typing>>,
the type checker could determine that it is ok to call `toUpperCase`. It is in fact an *error*, because a subclass can
override `compute` and return a different object. Here, `B#compute` returns an `int`, so someone calling `computeFully`
on an instance of `B` would see a runtime error. The compiler prevents this from happening by using the declared return
type of methods instead of the inferred return type.
For consistency, this behavior is the same for *every* method, even if they are static or final.
****
===== Parameter type inference
In addition to the return type, it is possible for a closure to infer its parameter types from the context. There are
two ways for the compiler to infer the parameter types:
* through _implicit SAM type coercion_
* through API metadata
To illustrate this, lets start with an example that will fail compilation due to the inability for the type checker
to infer the parameter types:
[source,groovy]
----
include::{projectdir}/src/spec/test/typing/TypeCheckingTest.groovy[tags=cl_pt_failure,indent=0]
----
<1> the `inviteIf` method accepts a `Person` and a `Closure`
<2> we call it with a `Person` and a `Closure`
<3> yet `it` is not statically known as being a `Person` and compilation fails
In this example, the closure body contains `it.age`. With dynamic, not type checked code, this would work, because the
type of `it` would be a `Person` at runtime. Unfortunately, at compile-time, there's no way to know what is the type
of `it`, just by reading the signature of `inviteIf`.
====== Explicit closure parameters
To be short, the type checker doesn't have enough contextual information on the `inviteIf` method to determine statically
the type of `it`. This means that the method call needs to be rewritten like this:
[source,groovy]
----
include::{projectdir}/src/spec/test/typing/TypeCheckingTest.groovy[tags=cl_pt_workaround,indent=0]
----
<1> the type of `it` needs to be declared explicitly
By explicitly declaring the type of the `it` variable, you can workaround the problem and make this code statically
checked.
====== Parameters inferred from single-abstract method types
For an API or framework designer, there are two ways to make this more elegant for users, so that they don't have to
declare an explicit type for the closure parameters. The first one, and easiest, is to replace the closure with a
SAM type:
[source,groovy]
----
include::{projectdir}/src/spec/test/typing/TypeCheckingTest.groovy[tags=cl_pt_workaround_sam,indent=0]
----
<1> declare a `SAM` interface with an `apply` method
<2> `inviteIf` now uses a `Predicate<Person>` instead of a `Closure<Boolean>`
<3> there's no need to declare the type of the `it` variable anymore
<4> `it.age` compiles properly, the type of `it` is inferred from the `Predicate#apply` method signature
TIP: By using this technique, we leverage the _automatic coercion of closures to SAM types_ feature of Groovy. The
question whether you should use a _SAM type_ or a _Closure_ really depends on what you need to do. In a lot of cases,
using a SAM interface is enough, especially if you consider functional interfaces as they are found in Java 8. However,
closures provide features that are not accessible to functional interfaces. In particular, closures can have a delegate,
and owner and can be manipulated as objects (for example, cloned, serialized, curried, ...) before being called. They can
also support multiple signatures (polymorphism). So if you need that kind of manipulation, it is preferable to switch to
the most advanced type inference annotations which are described below.
The original issue that needs to be solved when it comes to closure parameter type inference, that is to say, statically
determining the types of the arguments of a closure _without_ having to have them explicitly declared, is that the Groovy
type system inherits the Java type system, which is insufficient to describe the types of the arguments.
====== The `@ClosureParams` annotation
Groovy provides an annotation, `@ClosureParams` which is aimed at completing type information. This annotation is primarily
aimed at framework and API developers who want to extend the capabilities of the type checker by providing type inference
metadata. This is important if your library makes use of closures and that you want the maximum level of tooling support
too.
Let's illustrate this by fixing the original example, introducing the `@ClosureParams` annotation:
[source,groovy]
----
include::{projectdir}/src/spec/test/typing/TypeCheckingTest.groovy[tags=cl_pt_workaround_closureparams_imports,indent=0]
include::{projectdir}/src/spec/test/typing/TypeCheckingTest.groovy[tags=cl_pt_workaround_closureparams_method,indent=0]
include::{projectdir}/src/spec/test/typing/TypeCheckingTest.groovy[tags=cl_pt_workaround_closureparams_call,indent=0]
----
<1> the closure parameter is annotated with `@ClosureParams`
<2> it's not necessary to use an explicit type for `it`, which is inferred
The `@ClosureParams` annotation minimally accepts one argument, which is named a _type hint_. A type hint is a class which
is responsible for completing type information at compile time for the closure. In this example, the type hint being used
is `groovy.transform.stc.FirstParam` which indicated to the type checker that the closure will accept one parameter
whose type is the type of the first parameter of the method. In this case, the first parameter of the method is `Person`,
so it indicates to the type checker that the first parameter of the closure is in fact a `Person`.
A second optional argument is named _options_. It's semantics depend on the _type hint_ class. Groovy comes with
various bundled type hints, illustrated in the table below:
[cols="1a,1,4a"]
.Predefined type hints
|===
|Type hint |Polymorphic? |Description and examples
|`FirstParam` +
`SecondParam` +
`ThirdParam`
|No
|The first (resp. second, third) parameter type of the method +
[source,groovy]
----
include::{projectdir}/src/spec/test/typing/TypeCheckingHintsTest.groovy[tags=typehint_firstparam,indent=0]
----
[source,groovy]
----
include::{projectdir}/src/spec/test/typing/TypeCheckingHintsTest.groovy[tags=typehint_secondparam,indent=0]
----
[source,groovy]
----
include::{projectdir}/src/spec/test/typing/TypeCheckingHintsTest.groovy[tags=typehint_thirdparam,indent=0]
----
|`FirstParam.FirstGenericType` +
`SecondParam.FirstGenericType` +
`ThirdParam.FirstGenericType`
|No
|The first generic type of the first (resp. second, third) parameter of the method +
[source,groovy]
----
include::{projectdir}/src/spec/test/typing/TypeCheckingHintsTest.groovy[tags=typehint_firstgt,indent=0]
----
Variants for `SecondGenericType` and `ThirdGenericType` exist for all `FirstParam`, `SecondParam` and `ThirdParam`
type hints.
|`SimpleType`
|No
|A type hint for which the type of closure parameters comes from the options string. +
[source,groovy]
----
include::{projectdir}/src/spec/test/typing/TypeCheckingHintsTest.groovy[tags=typehint_simpletype,indent=0]
----
This type hint supports a *single* signature and each of the parameter is specified as a value of the _options_ array
using a fully-qualified type name or a primitive type.
|`MapEntryOrKeyValue`
|Yes
|A dedicated type hint for closures that either work on a `Map.Entry` single parameter, or two parameters corresponding
to the key and the value. +
[source,groovy]
----
include::{projectdir}/src/spec/test/typing/TypeCheckingHintsTest.groovy[tags=typehint_mapentry,indent=0]
----
This type hint *requires* that the first argument is a `Map` type, and infers the closure parameter types from the map
actual key/value types.
|`FromAbstractTypeMethods`
|Yes
|Infers closure parameter types from the abstract method of some type. A signature is inferred for *each* abstract method. +
[source,groovy]
----
include::{projectdir}/src/spec/test/typing/TypeCheckingHintsTest.groovy[tags=typehint_from_abstract_type,indent=0]
----
If there are multiple signatures like in the example above, the type checker will *only* be able to infer the types of
the arguments if the arity of each method is different. In the example above, `firstSignature` takes 2 arguments and
`secondSignature` takes 1 argument, so the type checker can infer the argument types based on the number of arguments.
But see the optional resolver class attribute discussed next.
|`FromString`
|Yes
|Infers the closure parameter types from the `options` argument. The `options` argument consists of an array of comma-separated
non-primitive types. Each element of the array corresponds to a single signature, and each comma in an element separate
parameters of the signature. In short, this is the most generic type hint, and each string of the `options` map is *parsed*
as if it was a signature literal. While being very powerful, this type hint must be avoided if you can because it increases
the compilation times due to the necessity of parsing the type signatures.
A single signature for a closure accepting a `String`:
[source,groovy]
----
include::{projectdir}/src/spec/test/typing/TypeCheckingHintsTest.groovy[tags=typehint_from_string_1,indent=0]
----
A polymorphic closure, accepting either a `String` or a `String, Integer`:
[source,groovy]
----
include::{projectdir}/src/spec/test/typing/TypeCheckingHintsTest.groovy[tags=typehint_from_string_2,indent=0]
----
A polymorphic closure, accepting either a `T` or a pair `T,T`:
[source,groovy]
----
include::{projectdir}/src/spec/test/typing/TypeCheckingHintsTest.groovy[tags=typehint_from_string_3,indent=0]
----
|===
TIP: Even though you use `FirstParam`, `SecondParam` or `ThirdParam` as a type hint, it doesn't strictly mean that the
argument which will be passed to the closure *will* be the first (resp. second, third) argument of the method call. It
only means that the *type* of the parameter of the closure will be the *same* as the type of the first (resp. second,
third) argument of the method call.
In short, the lack of the `@ClosureParams` annotation on a method accepting a `Closure` will *not* fail compilation. If
present (and it can be present in Java sources as well as Groovy sources), then the type checker has *more* information
and can perform additional type inference. This makes this feature particularly interesting for framework developers.
A third optional argument is named _conflictResolutionStrategy_. It can reference a class (extending from
`ClosureSignatureConflictResolver`) that can perform additional resolution of parameter types if more than
one are found after initial inference calculations are complete. Groovy comes with the a default type resolver
which does nothing, and another which selects the first signature if multiple are found. The resolver is
only invoked if more than one signature is found and is by design a post processor. Any statements which need
injected typing information must pass one of the parameter signatures determined through type hints. The
resolver then picks among the returned candidate signatures.
===== `@DelegatesTo`
The `@DelegatesTo` annotation is used by the type checker to infer the type of the delegate. It allows the API designer
to instruct the compiler what is the type of the delegate and the delegation strategy. The `@DelegatesTo` annotation is
discussed in a link:core-domain-specific-languages.html#section-delegatesto[specific section].
=== Static compilation
==== Dynamic vs static
In the <<static-type-checking,type checking section>>, we have seen that Groovy provides optional type checking thanks to the
`@TypeChecked` annotation. The type checker runs at compile time and performs a static analysis of dynamic code. The
program will behave exactly the same whether type checking has been enabled or not. This means that the `@TypeChecked`
annotation is neutral with regards to the semantics of a program. Even though it may be necessary to add type information
in the sources so that the program is considered type safe, in the end, the semantics of the program are the same.
While this may sound fine, there is actually one issue with this: type checking of dynamic code, done at compile time, is
by definition only correct if no runtime specific behavior occurs. For example, the following program passes type checking:
[[typechecked-defeated]]
[source,groovy]
----
include::{projectdir}/src/spec/test/typing/StaticCompilationIntroTest.groovy[tags=intro_typesafe,indent=0]
----
There are two `compute` methods. One accepts a `String` and returns an `int`, the other accepts an `int` and returns
a `String`. If you compile this, it is considered type safe: the inner `compute('foobar')` call will return an `int`,
and calling `compute` on this `int` will in turn return a `String`.
Now, before calling `test()`, consider adding the following line:
[source,groovy]
----
include::{projectdir}/src/spec/test/typing/StaticCompilationIntroTest.groovy[tags=intro_typesafe_magic,indent=0]
----
Using runtime metaprogramming, we're actually modifying the behavior of the `compute(String)` method, so that instead of
returning the length of the provided argument, it will return a `Date`. If you execute the program, it will fail at
runtime. Since this line can be added from anywhere, in any thread, there's absolutely no way for the type checker to
statically make sure that no such thing happens. In short, the type checker is vulnerable to monkey patching. This is
just one example, but this illustrates the concept that doing static analysis of a dynamic program is inherently wrong.
The Groovy language provides an alternative annotation to `@TypeChecked` which will actually make sure that the methods
which are inferred as being called *will* effectively be called at runtime. This annotation turns the Groovy compiler
into a *static compiler*, where all method calls are resolved at compile time *and* the generated bytecode makes sure
that this happens: the annotation is `@groovy.transform.CompileStatic`.
[[compilestatic-annotation]]
==== The `@CompileStatic` annotation
The `@CompileStatic` annotation can be added anywhere the `@TypeChecked` annotation can be used, that is to say on
a class or a method. It is not necessary to add both `@TypeChecked` and `@CompileStatic`, as `@CompileStatic` performs
everything `@TypeChecked` does, but in addition triggers static compilation.
Let's take the <<typechecked-defeated,example which failed>>, but this time let's replace the `@TypeChecked` annotation
with `@CompileStatic`:
[source,groovy]
----
include::{projectdir}/src/spec/test/typing/StaticCompilationIntroTest.groovy[tags=intro_typesafe_compilestatic,indent=0]
include::{projectdir}/src/spec/test/typing/StaticCompilationIntroTest.groovy[tags=intro_typesafe_magic,indent=0]
test()
----
This is the *only* difference. If we execute this program, this time, there is no runtime error. The `test` method
became immune to monkey patching, because the `compute` methods which are called in its body are linked at compile
time, so even if the metaclass of `Computer` changes, the program still behaves *as expected by the type checker*.
==== Key benefits
There are several benefits of using `@CompileStatic` on your code:
* type safety
* immunity to <<compilestatic-annotation,monkey patching>>
* performance improvements
The performance improvements depend on the kind of program you are executing. If it is I/O bound, the difference between
statically compiled code and dynamic code is barely noticeable. On highly CPU intensive code, since the bytecode which
is generated is very close, if not equal, to the one that Java would produce for an equivalent program, the performance
is greatly improved.
TIP: Using the _invokedynamic_ version of Groovy, which is accessible to people using JDK 7 and above, the performance
of the dynamic code should be very close to the performance of statically compiled code. Sometimes, it can even be faster!
There is only one way to determine which version you should choose: measuring. The reason is that depending on your program
*and* the JVM that you use, the performance can be significantly different. In particular, the _invokedynamic_ version of
Groovy is very sensitive to the JVM version in use.
include::type-checking-extensions.adoc[leveloffset=+1]