| ////////////////////////////////////////// |
| |
| 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. |
| |
| ////////////////////////////////////////// |
| ifndef::core-operators[] |
| :core-operators: core-operators.adoc |
| endif::[] |
| ifndef::core-semantics[] |
| :core-semantics: core-semantics.adoc |
| endif::[] |
| |
| = Syntax |
| |
| This chapter covers the syntax of the Groovy programming language. |
| The grammar of the language derives from the Java grammar, |
| but enhances it with specific constructs for Groovy, and allows certain simplifications. |
| |
| == Comments |
| |
| === Single-line comment |
| |
| Single-line comments start with `//` and can be found at any position in the line. |
| The characters following `//`, until the end of the line, are considered part of the comment. |
| |
| [source,groovy] |
| ---- |
| include::../test/SyntaxTest.groovy[tags=single_line_comment,indent=0] |
| ---- |
| |
| === Multiline comment |
| |
| A multiline comment starts with `/\*` and can be found at any position in the line. |
| The characters following `/*` will be considered part of the comment, including new line characters, |
| up to the first `*/` closing the comment. |
| Multiline comments can thus be put at the end of a statement, or even inside a statement. |
| |
| [source,groovy] |
| ---- |
| include::../test/SyntaxTest.groovy[tags=multiline_comment,indent=0] |
| ---- |
| |
| === Groovydoc comment |
| |
| Similarly to multiline comments, Groovydoc comments are multiline, but start with `/\**` and end with `*/`. |
| Lines following the first Groovydoc comment line can optionally start with a star `*`. |
| Those comments are associated with: |
| |
| * type definitions (classes, interfaces, enums, annotations), |
| * fields and properties definitions |
| * methods definitions |
| |
| Although the compiler will not complain about Groovydoc comments not being associated with the above language elements, |
| you should prepend those constructs with the comment right before it. |
| |
| [source,groovy] |
| ---- |
| include::../test/SyntaxTest.groovy[tags=groovydoc_comment,indent=0] |
| ---- |
| |
| Groovydoc follows the same conventions as Java's own Javadoc. |
| So you'll be able to use the same tags as with Javadoc. |
| |
| In addition, Groovy supports *Runtime Groovydoc* since 3.0.0, i.e. Groovydoc can be retained at runtime. |
| |
| NOTE: Runtime Groovydoc is disabled by default. It can be enabled by adding JVM option `-Dgroovy.attach.runtime.groovydoc=true` |
| |
| The Runtime Groovydoc starts with `/\**@` and ends with `*/`, for example: |
| |
| [source,groovy] |
| ---- |
| /**@ |
| * Some class groovydoc for Foo |
| */ |
| class Foo { |
| /**@ |
| * Some method groovydoc for bar |
| */ |
| void bar() { |
| } |
| } |
| |
| assert Foo.class.groovydoc.content.contains('Some class groovydoc for Foo') // <1> |
| assert Foo.class.getMethod('bar', new Class[0]).groovydoc.content.contains('Some method groovydoc for bar') // <2> |
| ---- |
| <1> Get the runtime groovydoc for class `Foo` |
| <2> Get the runtime groovydoc for method `bar` |
| |
| === Shebang line |
| |
| Beside the single-line comment, there is a special line comment, often called the _shebang_ line understood by UNIX systems |
| which allows scripts to be run directly from the command-line, provided you have installed the Groovy distribution |
| and the `groovy` command is available on the `PATH`. |
| |
| [source,groovy] |
| ---- |
| include::../test/SyntaxTest.groovy[tags=shebang_comment_line,indent=0] |
| ---- |
| |
| NOTE: The `#` character must be the first character of the file. Any indentation would yield a compilation error. |
| |
| == Keywords |
| |
| Groovy has the following reserved keywords: |
| |
| [cols="1,1,1,1"] |
| .Reserved Keywords |
| |=== |
| include::../test/SyntaxTest.groovy[tags=reserved_keywords,indent=0] |
| |=== |
| |
| Of these, `const`, `goto`, `strictfp`, and `threadsafe` are not currently in use. |
| |
| The reserved keywords can't in general be used for variable, field and method names. |
| |
| **** |
| A trick allows methods to be defined having the same name as a keyword |
| by surrounding the name in quotes as shown in the following example: |
| |
| [source,groovy] |
| ---- |
| include::../test/SyntaxTest.groovy[tags=reserved_keywords_example,indent=0] |
| ---- |
| |
| Using such names might be confusing and is often best to avoid. |
| The trick is primarily intended to enable certain Java integration scenarios |
| and certain link:core-domain-specific-languages.html[DSL] scenarios where |
| having "verbs" and "nouns" with the same name as keywords may be desirable. |
| **** |
| |
| In addition, Groovy has the following contextual keywords: |
| |
| [cols="1,1,1,1"] |
| .Contextual Keywords |
| |=== |
| include::../test/SyntaxTest.groovy[tags=contextual_keywords,indent=0] |
| |=== |
| |
| These words are only keywords in certain contexts and can be more freely used in some places, |
| in particular for variables, fields and method names. |
| |
| **** |
| This extra lenience allows using method or variable names that were not keywords in earlier |
| versions of Groovy or are not keywords in Java. Examples are shown here: |
| |
| [source,groovy] |
| ---- |
| include::../test/SyntaxTest.groovy[tags=contextual_keywords_examples,indent=0] |
| ---- |
| Groovy programmers familiar with these contextual keywords may still wish to avoid |
| using those names unless there is a good reason to use such a name. |
| **** |
| |
| The restrictions on reserved keywords also apply for the |
| primitive types, the boolean literals and the null literal (all of which are discussed later): |
| |
| [cols="1,1,1,1"] |
| .Other reserved words |
| |=== |
| include::../test/SyntaxTest.groovy[tags=reserved_words,indent=0] |
| |=== |
| |
| **** |
| While not recommended, the same trick as for reserved keywords can be used: |
| [source,groovy] |
| ---- |
| include::../test/SyntaxTest.groovy[tags=reserved_words_example,indent=0] |
| ---- |
| Using such words as method names is potentially confusing and is often best to avoid, however, |
| it might be useful for certain kinds of link:core-domain-specific-languages.html[DSLs]. |
| **** |
| |
| == Identifiers |
| |
| === Normal identifiers |
| |
| Identifiers start with a letter, a dollar or an underscore. |
| They cannot start with a number. |
| |
| A letter can be in the following ranges: |
| |
| * 'a' to 'z' (lowercase ascii letter) |
| * 'A' to 'Z' (uppercase ascii letter) |
| * '\u00C0' to '\u00D6' |
| * '\u00D8' to '\u00F6' |
| * '\u00F8' to '\u00FF' |
| * '\u0100' to '\uFFFE' |
| |
| Then following characters can contain letters and numbers. |
| |
| Here are a few examples of valid identifiers (here, variable names): |
| |
| [source,groovy] |
| ---- |
| include::../test/SyntaxTest.groovy[tags=valid_identifiers,indent=0] |
| ---- |
| |
| But the following ones are invalid identifiers: |
| |
| [source,groovy] |
| ---- |
| include::../test/SyntaxTest.groovy[tags=invalid_identifiers,indent=0] |
| ---- |
| |
| All keywords are also valid identifiers when following a dot: |
| |
| [source,groovy] |
| ---- |
| include::../test/SyntaxTest.groovy[tags=keywords_valid_id_after_dot,indent=0] |
| ---- |
| |
| === Quoted identifiers |
| |
| Quoted identifiers appear after the dot of a dotted expression. |
| For instance, the `name` part of the `person.name` expression can be quoted with `person."name"` or `person.'name'`. |
| This is particularly interesting when certain identifiers contain illegal characters that are forbidden by the Java Language Specification, |
| but which are allowed by Groovy when quoted. For example, characters like a dash, a space, an exclamation mark, etc. |
| |
| [source,groovy] |
| ---- |
| include::../test/SyntaxTest.groovy[tags=quoted_id,indent=0] |
| ---- |
| |
| As we shall see in the <<all-strings,following section on strings>>, Groovy provides different string literals. |
| All kind of strings are actually allowed after the dot: |
| |
| [source,groovy] |
| ---- |
| include::../test/SyntaxTest.groovy[tags=quoted_id_with_all_strings,indent=0] |
| ---- |
| |
| There's a difference between plain character strings and Groovy's GStrings (interpolated strings), |
| as in that the latter case, the interpolated values are inserted in the final string for evaluating the whole identifier: |
| |
| [source,groovy] |
| ---- |
| include::../test/SyntaxTest.groovy[tags=quoted_id_with_gstring,indent=0] |
| ---- |
| |
| [[all-strings]] |
| == Strings |
| |
| Text literals are represented in the form of chain of characters called strings. |
| Groovy lets you instantiate `java.lang.String` objects, as well as GStrings (`groovy.lang.GString`) |
| which are also called _interpolated strings_ in other programming languages. |
| |
| === Single-quoted string |
| |
| Single-quoted strings are a series of characters surrounded by single quotes: |
| |
| [source,groovy] |
| ---- |
| include::../test/SyntaxTest.groovy[tags=string_1,indent=0] |
| ---- |
| |
| NOTE: Single-quoted strings are plain `java.lang.String` and don't support interpolation. |
| |
| === String concatenation |
| |
| All the Groovy strings can be concatenated with the `+` operator: |
| |
| [source,groovy] |
| ---- |
| include::../test/SyntaxTest.groovy[tags=string_plus,indent=0] |
| ---- |
| |
| === Triple-single-quoted string |
| |
| Triple-single-quoted strings are a series of characters surrounded by triplets of single quotes: |
| |
| [source,groovy] |
| ---- |
| include::../test/SyntaxTest.groovy[tags=triple_single_0,indent=0] |
| ---- |
| |
| NOTE: Triple-single-quoted strings are plain `java.lang.String` and don't support interpolation. |
| |
| Triple-single-quoted strings may span multiple lines. |
| The content of the string can cross line boundaries without the need to split the string in several pieces |
| and without concatenation or newline escape characters: |
| |
| [source,groovy] |
| ---- |
| include::../test/SyntaxTest.groovy[tags=triple_single_1,indent=0] |
| ---- |
| |
| If your code is indented, for example in the body of the method of a class, your string will contain the whitespace of the indentation. |
| The Groovy Development Kit contains methods for stripping out the indentation with the `String#stripIndent()` method, |
| and with the `String#stripMargin()` method that takes a delimiter character to identify the text to remove from the beginning of a string. |
| |
| When creating a string as follows: |
| |
| [source,groovy] |
| ---- |
| include::../test/SyntaxTest.groovy[tags=triple_single_2,indent=0] |
| ---- |
| |
| You will notice that the resulting string contains a newline character as first character. |
| It is possible to strip that character by escaping the newline with a backslash: |
| |
| [source,groovy] |
| ---- |
| include::../test/SyntaxTest.groovy[tags=triple_single_3,indent=0] |
| ---- |
| |
| ==== Escaping special characters |
| |
| You can escape single quotes with the backslash character to avoid terminating the string literal: |
| |
| [source,groovy] |
| ---- |
| include::../test/SyntaxTest.groovy[tags=string_2,indent=0] |
| ---- |
| |
| And you can escape the escape character itself with a double backslash: |
| |
| [source,groovy] |
| ---- |
| include::../test/SyntaxTest.groovy[tags=string_3,indent=0] |
| ---- |
| |
| Some special characters also use the backslash as escape character: |
| |
| [cols="1,2" options="header"] |
| |==== |
| |Escape sequence |
| |Character |
| |
| |\b |
| |backspace |
| |
| |\f |
| |formfeed |
| |
| |\n |
| |newline |
| |
| |\r |
| |carriage return |
| |
| |\s |
| |single space |
| |
| |\t |
| |tabulation |
| |
| |\\ |
| |backslash |
| |
| |\' |
| |single quote within a single-quoted string (and optional for triple-single-quoted and double-quoted strings) |
| |
| |\" |
| |double quote within a double-quoted string (and optional for triple-double-quoted and single-quoted strings) |
| |==== |
| |
| We'll see some more escaping details when it comes to other types of strings discussed later. |
| |
| ==== Unicode escape sequence |
| |
| For characters that are not present on your keyboard, you can use unicode escape sequences: |
| a backslash, followed by 'u', then 4 hexadecimal digits. |
| |
| For example, the Euro currency symbol can be represented with: |
| |
| [source,groovy] |
| ---- |
| include::../test/SyntaxTest.groovy[tags=string_4,indent=0] |
| ---- |
| |
| === Double-quoted string |
| |
| Double-quoted strings are a series of characters surrounded by double quotes: |
| |
| [source,groovy] |
| ---- |
| include::../test/SyntaxTest.groovy[tags=string_5,indent=0] |
| ---- |
| |
| NOTE: Double-quoted strings are plain `java.lang.String` if there's no interpolated expression, |
| but are `groovy.lang.GString` instances if interpolation is present. |
| |
| NOTE: To escape a double quote, you can use the backslash character: +"A double quote: \""+. |
| |
| ==== String interpolation |
| |
| Any Groovy expression can be interpolated in all string literals, apart from single and triple-single-quoted strings. |
| Interpolation is the act of replacing a placeholder in the string with its value upon evaluation of the string. |
| The placeholder expressions are surrounded by `${}`. The curly braces may be omitted for unambiguous dotted expressions, |
| i.e. we can use just a $ prefix in those cases. |
| If the GString is ever passed to a method taking a String, the expression value inside the placeholder |
| is evaluated to its string representation (by calling `toString()` on that expression) and the resulting |
| String is passed to the method. |
| |
| Here, we have a string with a placeholder referencing a local variable: |
| |
| [source,groovy] |
| ---- |
| include::../test/SyntaxTest.groovy[tags=gstring_1,indent=0] |
| ---- |
| |
| Any Groovy expression is valid, as we can see in this example with an arithmetic expression: |
| |
| [source,groovy] |
| ---- |
| include::../test/SyntaxTest.groovy[tags=gstring_2,indent=0] |
| ---- |
| |
| [NOTE] |
| Not only are expressions allowed in between the `${}` placeholder, but so are statements. However, a statement's value is just `null`. |
| So if several statements are inserted in that placeholder, the last one should somehow return a meaningful value to be inserted. |
| For instance, +"The sum of 1 and 2 is equal to ${def a = 1; def b = 2; a + b}"+ is supported and works as expected but a good practice is usually to stick to simple expressions inside GString placeholders. |
| |
| In addition to `${}` placeholders, we can also use a lone `$` sign prefixing a dotted expression: |
| |
| [source,groovy] |
| ---- |
| include::../test/SyntaxTest.groovy[tags=gstring_3,indent=0] |
| ---- |
| |
| But only dotted expressions of the form `a.b`, `a.b.c`, etc, are valid. Expressions containing parentheses like method calls, |
| curly braces for closures, dots which aren't part of a property expression or arithmetic operators would be invalid. |
| Given the following variable definition of a number: |
| |
| [source,groovy] |
| ---- |
| include::../test/SyntaxTest.groovy[tags=gstring_4,indent=0] |
| ---- |
| |
| The following statement will throw a `groovy.lang.MissingPropertyException` because Groovy believes you're trying to access the `toString` property of that number, which doesn't exist: |
| |
| [source,groovy] |
| ---- |
| include::../test/SyntaxTest.groovy[tags=gstring_5,indent=0] |
| ---- |
| |
| NOTE: You can think of `"$number.toString()"` as being interpreted by the parser as `"${number.toString}()"`. |
| |
| Similarly, if the expression is ambiguous, you need to keep the curly braces: |
| |
| [source,groovy] |
| ---- |
| include::../test/SyntaxTest.groovy[tags=gstring_3b,indent=0] |
| include::../test/SyntaxTest.groovy[tags=gstring_3b2,indent=0] |
| include::../test/SyntaxTest.groovy[tags=gstring_3b3,indent=0] |
| ---- |
| |
| If you need to escape the `$` or `${}` placeholders in a GString so they appear as is without interpolation, |
| you just need to use a `\` backslash character to escape the dollar sign: |
| |
| [source,groovy] |
| ---- |
| include::../test/SyntaxTest.groovy[tags=gstring_6,indent=0] |
| ---- |
| |
| ==== Special case of interpolating closure expressions |
| |
| So far, we've seen we could interpolate arbitrary expressions inside the `${}` placeholder, but there is a special case and notation for closure expressions. When the placeholder contains an arrow, `${->}`, the expression is actually a closure expression -- you can think of it as a closure with a dollar prepended in front of it: |
| |
| [source,groovy] |
| ---- |
| include::../test/SyntaxTest.groovy[tags=closure_in_gstring_1,indent=0] |
| ---- |
| <1> The closure is a parameterless closure which doesn't take arguments. |
| <2> Here, the closure takes a single `java.io.StringWriter` argument, to which you can append content with the `<<` leftShift operator. |
| In either case, both placeholders are embedded closures. |
| |
| In appearance, it looks like a more verbose way of defining expressions to be interpolated, |
| but closures have an interesting advantage over mere expressions: lazy evaluation. |
| |
| Let's consider the following sample: |
| |
| [source,groovy] |
| ---- |
| include::../test/SyntaxTest.groovy[tags=closure_in_gstring_2,indent=0] |
| ---- |
| <1> We define a `number` variable containing `1` that we then interpolate within two GStrings, |
| as an expression in `eagerGString` and as a closure in `lazyGString`. |
| <2> We expect the resulting string to contain the same string value of 1 for `eagerGString`. |
| <3> Similarly for `lazyGString` |
| <4> Then we change the value of the variable to a new number |
| <5> With a plain interpolated expression, the value was actually bound at the time of creation of the GString. |
| <6> But with a closure expression, the closure is called upon each coercion of the GString into String, |
| resulting in an updated string containing the new number value. |
| |
| [NOTE] |
| An embedded closure expression taking more than one parameter will generate an exception at runtime. |
| Only closures with zero or one parameter are allowed. |
| |
| ==== Interoperability with Java |
| |
| When a method (whether implemented in Java or Groovy) expects a `java.lang.String`, |
| but we pass a `groovy.lang.GString` instance, |
| the `toString()` method of the GString is automatically and transparently called. |
| |
| [source,groovy] |
| ---- |
| include::../test/SyntaxTest.groovy[tags=java_gstring_interop_1,indent=0] |
| ---- |
| <1> We create a GString variable |
| <2> We double-check it's an instance of the GString |
| <3> We then pass that GString to a method taking a String as parameter |
| <4> The signature of the `takeString()` method explicitly says its sole parameter is a String |
| <5> We also verify that the parameter is indeed a String and not a GString. |
| |
| ==== GString and String hashCodes |
| |
| Although interpolated strings can be used in lieu of plain Java strings, |
| they differ with strings in a particular way: their hashCodes are different. |
| Plain Java strings are immutable, whereas the resulting String representation of a GString can vary, |
| depending on its interpolated values. |
| Even for the same resulting string, GStrings and Strings don't have the same hashCode. |
| |
| [source,groovy] |
| ---- |
| include::../test/SyntaxTest.groovy[tags=gstring_hashcode_1,indent=0] |
| ---- |
| |
| GString and Strings having different hashCode values, using GString as Map keys should be avoided, |
| especially if we try to retrieve an associated value with a String instead of a GString. |
| |
| [source,groovy] |
| ---- |
| include::../test/SyntaxTest.groovy[tags=gstring_hashcode_2,indent=0] |
| ---- |
| <1> The map is created with an initial pair whose key is a GString |
| <2> When we try to fetch the value with a String key, we will not find it, as Strings and GString have different hashCode values |
| |
| === Triple-double-quoted string |
| |
| Triple-double-quoted strings behave like double-quoted strings, with the addition that they are multiline, like the triple-single-quoted strings. |
| |
| [source,groovy] |
| ---- |
| include::../test/SyntaxTest.groovy[tags=triple_double_1,indent=0] |
| ---- |
| |
| NOTE: Neither double quotes nor single quotes need be escaped in triple-double-quoted strings. |
| |
| === Slashy string |
| |
| Beyond the usual quoted strings, Groovy offers slashy strings, which use `/` as the opening and closing delimiter. |
| Slashy strings are particularly useful for defining regular expressions and patterns, |
| as there is no need to escape backslashes. |
| |
| Example of a slashy string: |
| |
| [source,groovy] |
| ---- |
| include::../test/SyntaxTest.groovy[tags=slashy_1,indent=0] |
| ---- |
| |
| Only forward slashes need to be escaped with a backslash: |
| |
| [source,groovy] |
| ---- |
| include::../test/SyntaxTest.groovy[tags=slashy_2,indent=0] |
| ---- |
| |
| Slashy strings are multiline: |
| |
| [source,groovy] |
| ---- |
| include::../test/SyntaxTest.groovy[tags=slashy_3,indent=0] |
| ---- |
| |
| Slashy strings can be thought of as just another way to define a GString but with different escaping rules. They hence support interpolation: |
| |
| [source,groovy] |
| ---- |
| include::../test/SyntaxTest.groovy[tags=slashy_4,indent=0] |
| ---- |
| |
| ==== Special cases |
| |
| An empty slashy string cannot be represented with a double forward slash, as it's understood by the Groovy parser as a line comment. |
| That's why the following assert would actually not compile as it would look like a non-terminated statement: |
| |
| [source,groovy] |
| ---- |
| include::../test/SyntaxTest.groovy[tags=slashy_5,indent=0] |
| ---- |
| |
| As slashy strings were mostly designed to make regexp easier so a few things that |
| are errors in GStrings like `$()` or `$5` will work with slashy strings. |
| |
| Remember that escaping backslashes is not required. An alternative way of thinking of this is |
| that in fact escaping is not supported. The slashy string `/\t/` won't contain a tab but instead |
| a backslash followed by the character 't'. Escaping is only allowed for the slash character, i.e. `/\/folder/` |
| will be a slashy string containing `'/folder'`. A consequence of slash escaping is that a slashy string |
| can't end with a backslash. Otherwise that will escape the slashy string terminator. |
| You can instead use a special trick, `/ends with slash ${'\\'}/`. But best just avoid using a slashy string in such a case. |
| |
| === Dollar slashy string |
| |
| Dollar slashy strings are multiline GStrings delimited with an opening `$/` and a closing `/$`. |
| The escaping character is the dollar sign, and it can escape another dollar, or a forward slash. |
| Escaping for the dollar and forward slash characters is only needed where conflicts arise with |
| the special use of those characters. The characters `$foo` would normally indicate a GString |
| placeholder, so those four characters can be entered into a dollar slashy string by escaping the dollar, i.e. `$$foo`. |
| Similarly, you will need to escape a dollar slashy closing delimiter if you want it to appear in your string. |
| |
| Here are a few examples: |
| |
| [source,groovy] |
| ---- |
| include::../test/SyntaxTest.groovy[tags=dollar_slashy_1,indent=0] |
| ---- |
| |
| It was created to overcome some of the limitations of the slashy string escaping rules. |
| Use it when its escaping rules suit your string contents (typically if it has some slashes you don't want to escape). |
| |
| === String summary table |
| |
| [cols="5*", ptions="header"] |
| |==== |
| |String name |
| |String syntax |
| |Interpolated |
| |Multiline |
| |Escape character |
| |
| |Single-quoted |
| |`'...'` |
| |icon:check-empty[] |
| |icon:check-empty[] |
| |`\` |
| |
| |Triple-single-quoted |
| |`'''...'''` |
| |icon:check-empty[] |
| |icon:check[] |
| |`\` |
| |
| |Double-quoted |
| |`"..."` |
| |icon:check[] |
| |icon:check-empty[] |
| |`\` |
| |
| |Triple-double-quoted |
| |`"""..."""` |
| |icon:check[] |
| |icon:check[] |
| |`\` |
| |
| |Slashy |
| |`/.../` |
| |icon:check[] |
| |icon:check[] |
| |`\` |
| |
| |Dollar slashy |
| |`$/.../$` |
| |icon:check[] |
| |icon:check[] |
| |`$` |
| |==== |
| |
| === Characters |
| |
| Unlike Java, Groovy doesn't have an explicit character literal. |
| However, you can be explicit about making a Groovy string an actual character, by three different means: |
| |
| [source,groovy] |
| ---- |
| include::../test/SyntaxTest.groovy[tags=char,indent=0] |
| ---- |
| <1> by being explicit when declaring a variable holding the character by specifying the `char` type |
| <2> by using type coercion with the `as` operator |
| <3> by using a cast to char operation |
| |
| NOTE: The first option [conum,data-value=1]_1_ is interesting when the character is held in a variable, |
| while the other two ([conum,data-value=2]_2_ and [conum,data-value=3]_3_) are more interesting when a char value must be passed as argument of a method call. |
| |
| include::_working-with-numbers.adoc[leveloffset=+1] |
| |
| == Booleans |
| |
| Boolean is a special data type that is used to represent truth values: `true` and `false`. |
| Use this data type for simple flags that track true/false <<{core-operators}#_conditional_operators,conditions>>. |
| |
| Boolean values can be stored in variables, assigned into fields, just like any other data type: |
| |
| [source,groovy] |
| ---- |
| include::../test/SyntaxTest.groovy[tags=variable_store_boolean_value,indent=0] |
| ---- |
| |
| `true` and `false` are the only two primitive boolean values. |
| But more complex boolean expressions can be represented using <<{core-operators}#_logical_operators,logical operators>>. |
| |
| In addition, Groovy has <<{core-semantics}#the-groovy-truth,special rules>> (often referred to as _Groovy Truth_) |
| for coercing non-boolean objects to a boolean value. |
| |
| == Lists |
| |
| Groovy uses a comma-separated list of values, surrounded by square brackets, to denote lists. |
| Groovy lists are plain JDK `java.util.List`, as Groovy doesn't define its own collection classes. |
| The concrete list implementation used when defining list literals are `java.util.ArrayList` by default, |
| unless you decide to specify otherwise, as we shall see later on. |
| |
| [source,groovy] |
| ---- |
| include::../test/SyntaxTest.groovy[tags=list_1,indent=0] |
| ---- |
| <1> We define a list numbers delimited by commas and surrounded by square brackets, and we assign that list into a variable |
| <2> The list is an instance of Java's `java.util.List` interface |
| <3> The size of the list can be queried with the `size()` method, and shows our list contains 3 elements |
| |
| In the above example, we used a homogeneous list, but you can also create lists containing values of heterogeneous types: |
| |
| [source,groovy] |
| ---- |
| include::../test/SyntaxTest.groovy[tags=list_2,indent=0] |
| ---- |
| <1> Our list here contains a number, a string and a boolean value |
| |
| We mentioned that by default, list literals are actually instances of `java.util.ArrayList`, |
| but it is possible to use a different backing type for our lists, |
| thanks to using type coercion with the `as` operator, or with explicit type declaration for your variables: |
| |
| [source,groovy] |
| ---- |
| include::../test/SyntaxTest.groovy[tags=coercion_of_list,indent=0] |
| ---- |
| <1> We use coercion with the `as` operator to explicitly request a `java.util.LinkedList` implementation |
| <2> We can say that the variable holding the list literal is of type `java.util.LinkedList` |
| |
| You can access elements of the list with the `[]` subscript operator (both for reading and setting values) |
| with positive indices or negative indices to access elements from the end of the list, as well as with ranges, |
| and use the `<<` leftShift operator to append elements to a list: |
| |
| [source,groovy] |
| ---- |
| include::../test/SyntaxTest.groovy[tags=subscript_and_leftshift,indent=0] |
| ---- |
| <1> Access the first element of the list (zero-based counting) |
| <2> Access the last element of the list with a negative index: -1 is the first element from the end of the list |
| <3> Use an assignment to set a new value for the third element of the list |
| <4> Use the `<<` leftShift operator to append an element at the end of the list |
| <5> Access two elements at once, returning a new list containing those two elements |
| <6> Use a range to access a range of values from the list, from a start to an end element position |
| |
| As lists can be heterogeneous in nature, lists can also contain other lists to create multidimensional lists: |
| |
| [source,groovy] |
| ---- |
| include::../test/SyntaxTest.groovy[tags=multi_dim_list,indent=0] |
| ---- |
| <1> Define a list of numbers |
| <2> Access the second element of the top-most list, and the first element of the inner list |
| |
| == Arrays |
| |
| Groovy reuses the list notation for arrays, but to make such literals arrays, |
| you need to explicitly define the type of the array through coercion or type declaration. |
| |
| [source,groovy] |
| ---- |
| include::../test/SyntaxTest.groovy[tags=array_1,indent=0] |
| ---- |
| <1> Define an array of strings using explicit variable type declaration |
| <2> Assert that we created an array of strings |
| <3> Create an array of ints with the `as` operator |
| <4> Assert that we created an array of primitive ints |
| |
| You can also create multi-dimensional arrays: |
| |
| [source,groovy] |
| ---- |
| include::../test/SyntaxTest.groovy[tags=array_2,indent=0] |
| ---- |
| <1> You can define the bounds of a new array |
| <2> Or declare an array without specifying its bounds |
| |
| Access to elements of an array follows the same notation as for lists: |
| |
| [source,groovy] |
| ---- |
| include::../test/SyntaxTest.groovy[tags=array_3,indent=0] |
| ---- |
| <1> Retrieve the first element of the array |
| <2> Set the value of the third element of the array to a new value |
| |
| === Java-style array initialization |
| |
| Groovy has always supported literal list/array definitions using square brackets |
| and has avoided Java-style curly braces so as not to conflict with closure definitions. |
| In the case where the curly braces come immediately after an array type declaration however, |
| there is no ambiguity with closure definitions, |
| so Groovy 3 and above support that variant of the Java array initialization expression. |
| |
| Examples: |
| |
| [source,groovy] |
| -------------------------------------- |
| def primes = new int[] {2, 3, 5, 7, 11} |
| assert primes.size() == 5 && primes.sum() == 28 |
| assert primes.class.name == '[I' |
| |
| def pets = new String[] {'cat', 'dog'} |
| assert pets.size() == 2 && pets.sum() == 'catdog' |
| assert pets.class.name == '[Ljava.lang.String;' |
| |
| // traditional Groovy alternative still supported |
| String[] groovyBooks = [ 'Groovy in Action', 'Making Java Groovy' ] |
| assert groovyBooks.every{ it.contains('Groovy') } |
| -------------------------------------- |
| |
| |
| == Maps |
| |
| Sometimes called dictionaries or associative arrays in other languages, Groovy features maps. |
| Maps associate keys to values, separating keys and values with colons, and each key/value pairs with commas, |
| and the whole keys and values surrounded by square brackets. |
| |
| [source,groovy] |
| ---- |
| include::../test/SyntaxTest.groovy[tags=map_def_access,indent=0] |
| ---- |
| <1> We define a map of string color names, associated with their hexadecimal-coded html colors |
| <2> We use the subscript notation to check the content associated with the `red` key |
| <3> We can also use the property notation to assert the color green's hexadecimal representation |
| <4> Similarly, we can use the subscript notation to add a new key/value pair |
| <5> Or the property notation, to add the `yellow` color |
| |
| [NOTE] |
| When using names for the keys, we actually define string keys in the map. |
| |
| [NOTE] |
| Groovy creates maps that are actually instances of `java.util.LinkedHashMap`. |
| |
| If you try to access a key which is not present in the map: |
| |
| [source,groovy] |
| ---- |
| include::../test/SyntaxTest.groovy[tags=unknown_key,indent=0] |
| ---- |
| |
| You will retrieve a `null` result. |
| |
| In the examples above, we used string keys, but you can also use values of other types as keys: |
| |
| [source,groovy] |
| ---- |
| include::../test/SyntaxTest.groovy[tags=number_key,indent=0] |
| ---- |
| |
| Here, we used numbers as keys, as numbers can unambiguously be recognized as numbers, |
| so Groovy will not create a string key like in our previous examples. |
| But consider the case you want to pass a variable in lieu of the key, to have the value of that variable become the key: |
| |
| [source,groovy] |
| ---- |
| include::../test/SyntaxTest.groovy[tags=variable_key_1,indent=0] |
| ---- |
| <1> The `key` associated with the `'Guillaume'` name will actually be the `"key"` string, not the value associated with the `key` variable |
| <2> The map doesn't contain the `'name'` key |
| <3> Instead, the map contains a `'key'` key |
| |
| [NOTE] |
| You can also pass quoted strings as well as keys: +["name": "Guillaume"]+. |
| This is mandatory if your key string isn't a valid identifier, |
| for example if you wanted to create a string key containing a dash like in: +["street-name": "Main street"]+. |
| |
| When you need to pass variable values as keys in your map definitions, you must surround the variable or expression with parentheses: |
| |
| [source,groovy] |
| ---- |
| include::../test/SyntaxTest.groovy[tags=variable_key_2,indent=0] |
| ---- |
| <1> This time, we surround the `key` variable with parentheses, to instruct the parser we are passing a variable rather than defining a string key |
| <2> The map does contain the `name` key |
| <3> But the map doesn't contain the `key` key as before |