| ////////////////////////////////////////// |
| |
| 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. |
| |
| ////////////////////////////////////////// |
| |
| [[groovy-operators]] |
| = Operators |
| |
| This chapter covers the operators of the Groovy programming language. |
| |
| == Arithmetic operators |
| |
| Groovy supports the usual familiar arithmetic operators you find in mathematics and in other programming languages like Java. |
| All the Java arithmetic operators are supported. Let's go through them in the following examples. |
| |
| === Normal arithmetic operators |
| |
| The following binary arithmetic operators are available in Groovy: |
| |
| [cols="1,1,1" options="header"] |
| |==== |
| | Operator |
| | Purpose |
| | Remarks |
| |
| | `+` |
| | addition |
| | |
| |
| | `-` |
| | subtraction |
| | |
| |
| | `*` |
| | multiplication |
| | |
| |
| | `/` |
| | division |
| | Use `intdiv()` for integer division, and see the section about <<core-syntax.adoc#integer_division,integer division>> for more information on the return type of the division. |
| |
| | `%` |
| | remainder |
| | |
| |
| | `**` |
| | power |
| | See the section about <<core-syntax.adoc#power_operator,the power operation>> for more information on the return type of the operation. |
| |==== |
| |
| Here are a few examples of usage of those operators: |
| |
| [source,groovy] |
| ---- |
| include::{projectdir}/src/spec/test/OperatorsTest.groovy[tags=binary_arith_ops,indent=0] |
| ---- |
| |
| === Unary operators |
| |
| The `+` and `-` operators are also available as unary operators: |
| |
| [source,groovy] |
| ---- |
| include::{projectdir}/src/spec/test/OperatorsTest.groovy[tags=unary_plus_minus,indent=0] |
| ---- |
| <1> Note the usage of parentheses to surround an expression to apply the unary minus to that surrounded expression. |
| |
| In terms of unary arithmetics operators, the `++` (increment) and `--` (decrement) operators are available, |
| both in prefix and postfix notation: |
| |
| [source,groovy] |
| ---- |
| include::{projectdir}/src/spec/test/OperatorsTest.groovy[tags=plusplus_minusminus,indent=0] |
| ---- |
| <1> The postfix increment will increment `a` after the expression has been evaluated and assigned into `b` |
| <2> The postfix decrement will decrement `c` after the expression has been evaluated and assigned into `d` |
| <3> The prefix increment will increment `e` before the expression is evaluated and assigned into `f` |
| <4> The prefix decrement will decrement `g` before the expression is evaluated and assigned into `h` |
| |
| === Assignment arithmetic operators |
| |
| The binary arithmetic operators we have seen above are also available in an assignment form: |
| |
| * `+=` |
| * `-=` |
| * `*=` |
| * `/=` |
| * `%=` |
| * `**=` |
| |
| Let's see them in action: |
| |
| [source,groovy] |
| ---- |
| include::{projectdir}/src/spec/test/OperatorsTest.groovy[tags=binary_assign_operators,indent=0] |
| ---- |
| |
| == Relational operators |
| |
| Relational operators allow comparisons between objects, to know if two objects are the same or different, |
| or if one is greater than, less than, or equal to the other. |
| |
| The following operators are available: |
| |
| [cols="1,1" options="header"] |
| |==== |
| | Operator |
| | Purpose |
| |
| | `==` |
| | equal |
| |
| | `!=` |
| | different |
| |
| | `<` |
| | less than |
| |
| | `\<=` |
| | less than or equal |
| |
| | `>` |
| | greater than |
| |
| | `>=` |
| | greater than or equal |
| |==== |
| |
| Here are some examples of simple number comparisons using these operators: |
| |
| [source,groovy] |
| ---- |
| include::{projectdir}/src/spec/test/OperatorsTest.groovy[tags=simple_relational_op,indent=0] |
| ---- |
| |
| == Logical operators |
| |
| Groovy offers three logical operators for boolean expressions: |
| |
| * `&&`: logical "and" |
| * `||`: logical "or" |
| * `!`: logical "not" |
| |
| Let's illustrate them with the following examples: |
| |
| [source,groovy] |
| ---- |
| include::{projectdir}/src/spec/test/OperatorsTest.groovy[tags=logical_op,indent=0] |
| ---- |
| <1> "not" false is true |
| <2> true "and" true is true |
| <3> true "or" false is true |
| |
| === Precedence |
| |
| The logical "not" has a higher priority than the logical "and". |
| |
| [source,groovy] |
| ---- |
| include::{projectdir}/src/spec/test/OperatorsTest.groovy[tags=logical_precendence_1,indent=0] |
| ---- |
| <1> Here, the assertion is true (as the expression in parentheses is false), because "not" has a higher precedence than "and", so it only applies to the first "false" term; otherwise, it would have applied to the result of the "and", turned it into true, and the assertion would have failed |
| |
| The logical "and" has a higher priority than the logical "or". |
| |
| [source,groovy] |
| ---- |
| include::{projectdir}/src/spec/test/OperatorsTest.groovy[tags=logical_precendence_2,indent=0] |
| ---- |
| <1> Here, the assertion is true, because "and" has a higher precedence than "or", therefore the "or" is executed last and returns true, having one true argument; otherwise, the "and" would have executed last and returned false, having one false argument, and the assertion would have failed |
| |
| === Short-circuiting |
| |
| The logical `||` operator supports short-circuiting: if the left operand is true, it knows that the result will be true in any case, so it won't evaluate the right operand. |
| The right operand will be evaluated only if the left operand is false. |
| |
| Likewise for the logical `&&` operator: if the left operand is false, it knows that the result will be false in any case, so it won't evaluate the right operand. |
| The right operand will be evaluated only if the left operand is true. |
| |
| [source,groovy] |
| ---- |
| include::{projectdir}/src/spec/test/OperatorsTest.groovy[tags=logical_shortcircuit,indent=0] |
| ---- |
| <1> We create a function that sets the `called` flag to true whenever it's called |
| <2> In the first case, after resetting the called flag, we confirm that if the left operand to `||` is true, the function is not called, as `||` short-circuits the evaluation of the right operand |
| <3> In the second case, the left operand is false and so the function is called, as indicated by the fact our flag is now true |
| <4> Likewise for `&&`, we confirm that the function is not called with a false left operand |
| <5> But the function is called with a true left operand |
| |
| == Bitwise operators |
| |
| Groovy offers 4 bitwise operators: |
| |
| * `&`: bitwise "and" |
| * `|`: bitwise "or" |
| * `^`: bitwise "xor" (exclusive "or") |
| * `~`: bitwise negation |
| |
| Bitwise operators can be applied on a `byte` or an `int` and return an `int`: |
| |
| [source,groovy] |
| ---- |
| include::{projectdir}/src/spec/test/OperatorsTest.groovy[tags=bitwise_op,indent=0] |
| ---- |
| <1> bitwise and |
| <2> bitwise and returns common bits |
| <3> bitwise or |
| <4> bitwise or returns all '1' bits |
| <5> setting a mask to check only the last 8 bits |
| <6> bitwise exclusive or on self returns 0 |
| <7> bitwise exclusive or |
| <8> bitwise negation |
| |
| It's worth noting that the internal representation of primitive types follow the http://docs.oracle.com/javase/specs/jls/se8/html/jls-4.html[Java Language Specification]. In particular, |
| primitive types are signed, meaning that for a bitwise negation, it is always good to use a mask to retrieve only the necessary bits. |
| |
| In Groovy, bitwise operators have the particularity of being <<Operator-Overloading,overloadable>>, meaning that you can define the behavior of those operators for any kind of object. |
| |
| == Conditional operators |
| |
| === Not operator |
| |
| The "not" operator is represented with an exclamation mark (`!`) and inverts the result of the underlying boolean expression. In |
| particular, it is possible to combine the `not` operator with the <<core-semantics.adoc#Groovy-Truth,Groovy truth>>: |
| |
| [source,groovy] |
| ---- |
| include::{projectdir}/src/spec/test/OperatorsTest.groovy[tags=conditional_op_not,indent=0] |
| ---- |
| <1> the negation of `true` is `false` |
| <2> 'foo' is a non empty string, evaluating to `true`, so negation returns `false` |
| <3> '' is an empty string, evaluating to `false`, so negation returns `true` |
| |
| === Ternary operator |
| |
| The ternary operator is a shortcut expression that is equivalent to an if/else branch assigning some value to a variable. |
| |
| Instead of: |
| [source,groovy] |
| ---- |
| include::{projectdir}/src/spec/test/OperatorsTest.groovy[tags=conditional_op_ternary_if,indent=0] |
| ---- |
| |
| You can write: |
| [source,groovy] |
| ---- |
| include::{projectdir}/src/spec/test/OperatorsTest.groovy[tags=conditional_op_ternary_ternary,indent=0] |
| ---- |
| |
| The ternary operator is also compatible with the <<core-semantics.adoc#Groovy-Truth,Groovy truth>>, so you can make it even simpler: |
| |
| [source,groovy] |
| ---- |
| include::{projectdir}/src/spec/test/OperatorsTest.groovy[tags=conditional_op_ternary_groovytruth,indent=0] |
| ---- |
| |
| === Elvis operator |
| |
| The "Elvis operator" is a shortening of the ternary operator. One instance of where this is handy is for returning |
| a 'sensible default' value if an expression resolves to `false`-ish (as in |
| <<core-semantics.adoc#Groovy-Truth,Groovy truth>>). A simple example might look like this: |
| |
| [source,groovy] |
| ---- |
| include::{projectdir}/src/spec/test/OperatorsTest.groovy[tags=conditional_op_elvis,indent=0] |
| ---- |
| <1> with the ternary operator, you have to repeat the value you want to assign |
| <2> with the Elvis operator, the value, which is tested, is used if it is not `false`-ish |
| |
| Usage of the Elvis operator reduces the verbosity of your code and reduces the risks of errors in case of refactorings, |
| by removing the need to duplicate the expression which is tested in both the condition and the positive return value. |
| |
| == Object operators |
| |
| === Safe navigation operator |
| |
| The Safe Navigation operator is used to avoid a `NullPointerException`. Typically when you have a reference to an object |
| you might need to verify that it is not `null` before accessing methods or properties of the object. To avoid this, the safe |
| navigation operator will simply return `null` instead of throwing an exception, like so: |
| |
| [source,groovy] |
| ---- |
| include::{projectdir}/src/spec/test/OperatorsTest.groovy[tags=nullsafe,indent=0] |
| ---- |
| <1> `find` will return a `null` instance |
| <2> use of the null-safe operator prevents from a `NullPointerException` |
| <3> result is `null` |
| |
| === Direct field access operator |
| |
| Normally in Groovy, when you write code like this: |
| |
| [source,groovy] |
| ---- |
| include::{projectdir}/src/spec/test/OperatorsTest.groovy[tags=direct_field_class,indent=0] |
| ---- |
| <1> public field `name` |
| <2> a getter for `name` that returns a custom string |
| <3> calls the getter |
| |
| The `user.name` call triggers a call to the property of the same name, that is to say, here, to the getter for `name`. If |
| you want to retrieve the field instead of calling the getter, you can use the direct field access operator: |
| |
| [source,groovy] |
| ---- |
| include::{projectdir}/src/spec/test/OperatorsTest.groovy[tags=direct_field_op,indent=0] |
| ---- |
| <1> use of `.@` forces usage of the field instead of the getter |
| |
| [[method-pointer-operator]] |
| === Method pointer operator |
| |
| The method pointer operator (`.&`) can be used to store a reference to a method in a variable, |
| in order to call it later: |
| |
| [source,groovy] |
| ---- |
| include::{projectdir}/src/spec/test/OperatorsTest.groovy[tags=method_pointer,indent=0] |
| ---- |
| <1> the `str` variable contains a `String` |
| <2> we store a reference to the `toUpperCase` method on the `str` instance inside a variable named `fun` |
| <3> `fun` can be called like a regular method |
| <4> we can check that the result is the same as if we had called it directly on `str` |
| |
| There are multiple advantages in using method pointers. First of all, the type of such a method pointer is |
| a `groovy.lang.Closure`, so it can be used in any place a closure would be used. In particular, it is suitable to |
| convert an existing method for the needs of the strategy pattern: |
| |
| [source,groovy] |
| ---- |
| include::{projectdir}/src/spec/test/OperatorsTest.groovy[tags=method_pointer_strategy,indent=0] |
| ---- |
| <1> the `transform` method takes each element of the list and calls the `action` closure on them, returning a new list |
| <2> we define a function that takes a `Person` and returns a `String` |
| <3> we create a method pointer on that function |
| <4> we create the list of elements we want to collect the descriptors |
| <5> the method pointer can be used where a `Closure` was expected |
| |
| Method pointers are bound by the receiver and a method name. Arguments are resolved at runtime, meaning that if you have |
| multiple methods with the same name, the syntax is not different, only resolution of the appropriate method to be called |
| will be done at runtime: |
| |
| [source,groovy] |
| ---- |
| include::{projectdir}/src/spec/test/OperatorsTest.groovy[tags=method_pointer_dispatch,indent=0] |
| ---- |
| <1> define an overloaded `doSomething` method accepting a `String` as an argument |
| <2> define an overloaded `doSomething` method accepting an `Integer` as an argument |
| <3> create a single method pointer on `doSomething`, without specifying argument types |
| <4> using the method pointer with a `String` calls the `String` version of `doSomething` |
| <5> using the method pointer with an `Integer` calls the `Integer` version of `doSomething` |
| |
| To align with Java 8 method reference expectations, in Groovy 3 and above, you can use `new` as the |
| method name to obtain a method pointer to the constructor: |
| [source,groovy] |
| ---- |
| include::{projectdir}/src/spec/test/OperatorsTest.groovy[tags=method_pointer_new,indent=0] |
| ---- |
| |
| Also in Groovy 3 and above, you can obtain a method pointer to an instance method of a class. |
| This method pointer takes an additional parameter being the receiver instance to |
| invoke the method on: |
| [source,groovy] |
| ---- |
| include::{projectdir}/src/spec/test/OperatorsTest.groovy[tags=method_pointer_class_instance,indent=0] |
| ---- |
| For backwards compatibility, any static methods that happen to have the correct |
| parameters for the call will be given precedence over instance methods for this case. |
| |
| [[method-reference-operator]] |
| === Method reference operator |
| |
| The Parrot parser in Groovy 3+ supports the Java 8+ method reference operator. |
| The method reference operator (`::`) can be used to reference a method or constructor |
| in contexts expecting a functional interface. This overlaps somewhat with the functionality |
| provided by Groovy's method pointer operator. Indeed, for dynamic Groovy, the method |
| reference operator is just an alias for the method pointer operator. |
| For static Groovy, the operator results in bytecode similar to the bytecode |
| that Java would produce for the same context. |
| |
| Some examples highlighting various supported method reference cases are shown in the following script: |
| |
| [source,groovy] |
| ---- |
| include::{projectdir}/src/spec/test/OperatorsTest.groovy[tags=method_refs,indent=0] |
| ---- |
| <1> class instance method reference: add(BigInteger val) is an instance method in BigInteger |
| <2> object instance method reference: add(BigInteger val) is an instance method for object 3G |
| <3> class static method reference: valueOf(long val) is a static method for class BigInteger |
| <4> object static method reference: valueOf(long val) is a static method for object 3G (some consider this bad style in normal circumstances) |
| |
| Some examples highlighting various supported constructor reference cases are shown in the following script: |
| |
| [source,groovy] |
| ---- |
| include::{projectdir}/src/spec/test/OperatorsTest.groovy[tags=constructor_refs,indent=0] |
| ---- |
| <1> class constructor reference |
| <2> array constructor reference |
| |
| == Regular expression operators |
| |
| === Pattern operator |
| |
| The pattern operator (`~`) provides a simple way to create a `java.util.regex.Pattern` instance: |
| |
| [source,groovy] |
| ---- |
| include::{projectdir}/src/spec/test/OperatorsTest.groovy[tags=pattern_op,indent=0] |
| ---- |
| |
| while in general, you find the pattern operator with an expression in a slashy-string, it can be used with any kind of |
| `String` in Groovy: |
| |
| [source,groovy] |
| ---- |
| include::{projectdir}/src/spec/test/OperatorsTest.groovy[tags=pattern_op_variants,indent=0] |
| ---- |
| <1> using single quote strings |
| <2> using double quotes strings |
| <3> the dollar-slashy string lets you use slashes and the dollar sign without having to escape them |
| <4> you can also use a GString! |
| |
| === Find operator |
| |
| Alternatively to building a pattern, you can use the find operator `=~` to directly create a `java.util.regex.Matcher` instance: |
| |
| [source,groovy] |
| ---- |
| include::{projectdir}/src/spec/test/OperatorsTest.groovy[tags=pattern_matcher_op,indent=0] |
| ---- |
| <1> `=~` creates a matcher against the `text` variable, using the pattern on the right hand side |
| <2> the return type of `=~` is a `Matcher` |
| <3> equivalent to calling `if (!m.find(0))` |
| |
| Since a `Matcher` coerces to a `boolean` by calling its `find` method, the `=~` operator is consistent with the simple |
| use of Perl's `=~` operator, when it appears as a predicate (in `if`, `?:`, etc.). When the intent is to iterate over |
| matches of the specified pattern (in `while`, etc.) call `find()` directly on the matcher or use the `iterator` DGM. |
| |
| === Match operator |
| |
| The match operator (`==~`) is a slight variation of the find operator, that does not return a `Matcher` but a boolean |
| and requires a strict match of the input string: |
| |
| [source,groovy] |
| ---- |
| include::{projectdir}/src/spec/test/OperatorsTest.groovy[tags=pattern_matcher_strict_op,indent=0] |
| ---- |
| <1> `==~` matches the subject with the regular expression, but match must be strict |
| <2> the return type of `==~` is therefore a `boolean` |
| <3> equivalent to calling `if (text ==~ /match/)` |
| |
| == Other operators |
| |
| === Spread operator |
| |
| The Spread-dot Operator (`*.`), often abbreviated to just Spread Operator, is used to invoke an action on all items |
| of an aggregate object. It is equivalent to calling the action on each item and collecting the result into a list: |
| |
| [source,groovy] |
| ---- |
| include::{projectdir}/src/spec/test/OperatorsTest.groovy[tags=spreaddot,indent=0] |
| ---- |
| <1> build a list of `Car` items. The list is an aggregate of objects. |
| <2> call the spread operator on the list, accessing the `make` property of each item |
| <3> returns a list of strings corresponding to the collection of `make` items |
| |
| The expression `cars*.make` is equivalent to `cars.collect{ it.make }`. |
| Groovy's GPath notation allows a short-cut when the referenced property |
| isn't a property of the containing list, in that case it is automatically |
| spread. In the previously mentioned case, the expression `cars.make` can |
| be used, though retaining the explicit spread-dot operator is often recommended. |
| |
| The spread operator is null-safe, meaning that if an element of the collection is null, |
| it will return null instead of throwing a `NullPointerException`: |
| |
| [source,groovy] |
| ---- |
| include::{projectdir}/src/spec/test/OperatorsTest.groovy[tags=spreaddot_nullsafe,indent=0] |
| ---- |
| <1> build a list for which of of the elements is `null` |
| <2> using the spread operator will *not* throw a `NullPointerException` |
| <3> the receiver might also be null, in which case the return value is `null` |
| |
| The spread operator can be used on any class which implements the `Iterable` interface: |
| |
| [source,groovy] |
| ---- |
| include::{projectdir}/src/spec/test/OperatorsTest.groovy[tags=spreaddot_iterable,indent=0] |
| ---- |
| |
| Use multiple invocations of the spread-dot operator (here `cars*.models*.name`) when |
| working with aggregates of data structures which themselves contain aggregates: |
| |
| [source,groovy] |
| ---- |
| include::{projectdir}/src/spec/test/OperatorsTest.groovy[tags=spreaddot_multilevel,indent=0] |
| ---- |
| |
| Consider using the `collectNested` DGM method instead of the spread-dot operator for collections of collections: |
| |
| [source,groovy] |
| ---- |
| include::{projectdir}/src/spec/test/OperatorsTest.groovy[tags=spreaddot_alternative,indent=0] |
| ---- |
| |
| ==== Spreading method arguments |
| |
| There may be situations when the arguments of a method call can be found in a list that you need to adapt to the method |
| arguments. In such situations, you can use the spread operator to call the method. For example, imagine you have the |
| following method signature: |
| |
| [source,groovy] |
| ---- |
| include::{projectdir}/src/spec/test/OperatorsTest.groovy[tags=spreadmethodargs_method,indent=0] |
| ---- |
| |
| then if you have the following list: |
| |
| [source,groovy] |
| ---- |
| include::{projectdir}/src/spec/test/OperatorsTest.groovy[tags=spreadmethodargs_args,indent=0] |
| ---- |
| |
| you can call the method without having to define intermediate variables: |
| |
| [source,groovy] |
| ---- |
| include::{projectdir}/src/spec/test/OperatorsTest.groovy[tags=spreadmethodargs_assert,indent=0] |
| ---- |
| |
| It is even possible to mix normal arguments with spread ones: |
| |
| [source,groovy] |
| ---- |
| include::{projectdir}/src/spec/test/OperatorsTest.groovy[tags=spreadmethodargs_mixed,indent=0] |
| ---- |
| |
| ==== Spread list elements |
| |
| When used inside a list literal, the spread operator acts as if the spread element contents were inlined into the list: |
| |
| [source,groovy] |
| ---- |
| include::{projectdir}/src/spec/test/OperatorsTest.groovy[tags=spread_list,indent=0] |
| ---- |
| <1> `items` is a list |
| <2> we want to insert the contents of the `items` list directly into `list` without having to call `addAll` |
| <3> the contents of `items` has been inlined into `list` |
| |
| ==== Spread map elements |
| |
| The spread map operator works in a similar manner as the spread list operator, but for maps. It allows you to inline |
| the contents of a map into another map literal, like in the following example: |
| |
| [source,groovy] |
| ---- |
| include::{projectdir}/src/spec/test/OperatorsTest.groovy[tags=spread_map,indent=0] |
| ---- |
| <1> `m1` is the map that we want to inline |
| <2> we use the `*:m1` notation to spread the contents of `m1` into `map` |
| <3> `map` contains all the elements of `m1` |
| |
| The position of the spread map operator is relevant, like illustrated in the following example: |
| |
| [source,groovy] |
| ---- |
| include::{projectdir}/src/spec/test/OperatorsTest.groovy[tags=spread_map_position,indent=0] |
| ---- |
| <1> `m1` is the map that we want to inline |
| <2> we use the `*:m1` notation to spread the contents of `m1` into `map`, but redefine the key `d` **after** spreading |
| <3> `map` contains all the expected keys, but `d` was redefined |
| |
| === Range operator |
| |
| Groovy supports the concept of ranges and provides a notation (`..`) to create ranges of objects: |
| |
| [source,groovy] |
| ---- |
| include::{projectdir}/src/spec/test/OperatorsTest.groovy[tags=intrange,indent=0] |
| ---- |
| <1> a simple range of integers, stored into a local variable |
| <2> an `IntRange`, with inclusive bounds |
| <3> an `IntRange`, with exclusive upper bound |
| <4> a `groovy.lang.Range` implements the `List` interface |
| <5> meaning that you can call the `size` method on it |
| |
| Ranges implementation is lightweight, meaning that only the lower and upper bounds are stored. You can create a range |
| from any `Comparable` object that has `next()` and `previous()` methods to determine the next / previous item in the range. |
| For example, you can create a range of characters this way: |
| |
| [source,groovy] |
| ---- |
| include::{projectdir}/src/spec/test/OperatorsTest.groovy[tags=charrange,indent=0] |
| ---- |
| |
| === Spaceship operator |
| |
| The spaceship operator (`pass:[<=>]`) delegates to the `compareTo` method: |
| |
| [source,groovy] |
| ---- |
| include::{projectdir}/src/spec/test/OperatorsTest.groovy[tags=spaceship,indent=0] |
| ---- |
| |
| [[subscript-operator]] |
| === Subscript operator |
| |
| The subscript operator is a short hand notation for `getAt` or `putAt`, depending on whether you find it on |
| the left hand side or the right hand side of an assignment: |
| |
| [source,groovy] |
| ---- |
| include::{projectdir}/src/spec/test/OperatorsTest.groovy[tags=subscript_op,indent=0] |
| ---- |
| <1> `[2]` can be used instead of `getAt(2)` |
| <2> if on left hand side of an assignment, will call `putAt` |
| <3> `getAt` also supports ranges |
| <4> so does `putAt` |
| <5> the list is mutated |
| |
| [[custom-subscript-operator]] |
| The subscript operator, in combination with a custom implementation of `getAt`/`putAt` is a convenient way for destructuring |
| objects: |
| |
| [source,groovy] |
| ---- |
| include::{projectdir}/src/spec/test/OperatorsTest.groovy[tags=subscript_destructuring,indent=0] |
| ---- |
| <1> the `User` class defines a custom `getAt` implementation |
| <2> the `User` class defines a custom `putAt` implementation |
| <3> create a sample user |
| <4> using the subscript operator with index 0 allows retrieving the user id |
| <5> using the subscript operator with index 1 allows retrieving the user name |
| <6> we can use the subscript operator to write to a property thanks to the delegation to `putAt` |
| <7> and check that it's really the property `name` which was changed |
| |
| === Membership operator |
| |
| The membership operator (`in`) is equivalent to calling the `isCase` method. In the context of a `List`, it is equivalent |
| to calling `contains`, like in the following example: |
| |
| [source,groovy] |
| ---- |
| include::{projectdir}/src/spec/test/OperatorsTest.groovy[tags=membership_op,indent=0] |
| ---- |
| <1> equivalent to calling `list.contains('Emmy')` or `list.isCase('Emmy')` |
| |
| === Identity operator |
| |
| In Groovy, using `==` to test equality is different from using the same operator in Java. In Groovy, it is calling `equals`. |
| If you want to compare reference equality, you should use `is` like in the following example: |
| |
| [source,groovy] |
| ---- |
| include::{projectdir}/src/spec/test/OperatorsTest.groovy[tags=identity_op,indent=0] |
| ---- |
| <1> Create a list of strings |
| <2> Create another list of strings containing the same elements |
| <3> using `==`, we test object equality |
| <4> but using `is`, we can check that references are distinct |
| |
| === Coercion operator |
| |
| The coercion operator (`as`) is a variant of casting. Coercion converts object from one type to another *without* them |
| being compatible for assignment. Let's take an example: |
| |
| [source,groovy] |
| ---- |
| include::{projectdir}/src/spec/test/OperatorsTest.groovy[tags=coerce_op_cast,indent=0] |
| ---- |
| <1> `Integer` is not assignable to a `String`, so it will produce a `ClassCastException` at runtime |
| |
| This can be fixed by using _coercion_ instead: |
| |
| [source,groovy] |
| ---- |
| include::{projectdir}/src/spec/test/OperatorsTest.groovy[tags=coerce_op,indent=0] |
| ---- |
| <1> `Integer` is not assignable to a `String`, but use of `as` will _coerce_ it to a `String` |
| |
| When an object is coerced into another, unless the target type is the same as the source type, coercion will return a |
| *new* object. The rules of coercion differ depending on the source and target types, and coercion may fail if no conversion |
| rules are found. Custom conversion rules may be implemented thanks to the `asType` method: |
| |
| [source,groovy] |
| ---- |
| include::{projectdir}/src/spec/test/OperatorsTest.groovy[tags=coerce_op_custom,indent=0] |
| ---- |
| <1> the `User` class defines a custom conversion rule from `User` to `Identifiable` |
| <2> we create an instance of `User` |
| <3> we coerce the `User` instance into an `Identifiable` |
| <4> the target is an instance of `Identifiable` |
| <5> the target is not an instance of `User` anymore |
| |
| === Diamond operator |
| |
| The diamond operator (`<>`) is a syntactic sugar only operator added to support compatibility with the operator of the |
| same name in Java 7. It is used to indicate that generic types should be inferred from the declaration: |
| |
| [source,groovy] |
| ---- |
| include::{projectdir}/src/spec/test/OperatorsTest.groovy[tags=diamond_op,indent=0] |
| ---- |
| |
| In dynamic Groovy, this is totally unused. In statically type checked Groovy, it is also optional since the Groovy |
| type checker performs type inference whether this operator is present or not. |
| |
| === Call operator |
| |
| The call operator `()` is used to call a method named `call` implicitly. For any object which defines a `call` method, |
| you can omit the `.call` part and use the call operator instead: |
| |
| [source,groovy] |
| ---- |
| include::{projectdir}/src/spec/test/OperatorsTest.groovy[tags=call_op,indent=0] |
| ---- |
| <1> `MyCallable` defines a method named `call`. Note that it doesn't need to implement `java.util.concurrent.Callable` |
| <2> we can call the method using the classic method call syntax |
| <3> or we can omit `.call` thanks to the call operator |
| |
| == Operator precedence |
| The table below lists all groovy operators in order of precedence. |
| |
| [options="header"] |
| |====================== |
| |Level | Operator(s) | Name(s) |
| | 1 | `new`   `()` | object creation, explicit parentheses |
| | | `()`   `{}`   `[]` | method call, closure, literal list/map |
| | | `.`   `.&`   `.@` | member access, method closure, field/attribute access |
| | | `?.`   `\*`   `*.`   `*:` | safe dereferencing, spread, spread-dot, spread-map |
| | | `~`   `!`   `(type)` | bitwise negate/pattern, not, typecast |
| | | `[]`   `++`   `--` | list/map/array index, post inc/decrement |
| | 2 | `**` | power |
| | 3 | `++`   `--`   `+`   `-` | pre inc/decrement, unary plus, unary minus |
| | 4 | `*`   `/`   `%` | multiply, div, remainder |
| | 5 | `+`   `-` | addition, subtraction |
| | 6 | `<<`   `>>`   `>>>`   `..`   `..<` | left/right (unsigned) shift, inclusive/exclusive range |
| | 7 | `<`   `\<=`   `>`   `>=`   `in`   `instanceof`   `as` | less/greater than/or equal, in, instanceof, type coercion |
| | 8 | `==`   `!=`   `pass:[<=>]` | equals, not equals, compare to |
| | | `=~`   `==~` | regex find, regex match |
| | 9 | `&` | binary/bitwise and |
| | 10 | `^` | binary/bitwise xor |
| | 11 | `\|` | binary/bitwise or |
| | 12 | `&&` | logical and |
| | 13 | `\|\|` | logical or |
| | 14 | `? :` | ternary conditional |
| | | `?:` | elvis operator |
| | 15 | `=`   `\**=`   `*=`   `/=`   `%=`   `+=`   `-=`   + |
| `pass:[<<=]`   `>>=`   `>>>=`   `&=`   `^=`   `\|=` | various assignments |
| |====================== |
| |
| [[Operator-Overloading]] |
| == Operator overloading |
| |
| Groovy allows you to overload the various operators so that they can be used with your own classes. Consider this simple |
| class: |
| |
| [source,groovy] |
| ---- |
| include::{projectdir}/src/spec/test/OperatorsTest.groovy[tags=operator_overload_class,indent=0] |
| ---- |
| <1> `Bucket` implements a special method called `plus()` |
| |
| Just by implementing the `plus()` method, the `Bucket` class can now be used with the `+` operator like so: |
| |
| [source,groovy] |
| ---- |
| include::{projectdir}/src/spec/test/OperatorsTest.groovy[tags=operator_overload_op,indent=0] |
| ---- |
| <1> The two `Bucket` objects can be added together with the `+` operator |
| |
| All (non-comparator) Groovy operators have a corresponding method that you can implement in your own classes. The only |
| requirements are that your method is public, has the correct name, and has the correct number of arguments. The argument |
| types depend on what types you want to support on the right hand side of the operator. For example, you could support |
| the statement |
| |
| [source,groovy] |
| ---- |
| include::{projectdir}/src/spec/test/OperatorsTest.groovy[tags=operator_overload_mixed_op,indent=0] |
| ---- |
| |
| by implementing the `plus()` method with this signature: |
| |
| [source,groovy] |
| ---- |
| include::{projectdir}/src/spec/test/OperatorsTest.groovy[tags=operator_overload_mixed_class,indent=0] |
| ---- |
| |
| Here is a complete list of the operators and their corresponding methods: |
| |
| [cols="1,1,1,1" options="header"] |
| |==== |
| | Operator |
| | Method |
| | Operator |
| | Method |
| |
| | `+` |
| | a.plus(b) |
| | `a[b]` |
| | a.getAt(b) |
| |
| | `-` |
| | a.minus(b) |
| | `a[b] = c` |
| | a.putAt(b, c) |
| |
| | `*` |
| | a.multiply(b) |
| | `a in b` |
| | b.isCase(a) |
| |
| | `/` |
| | a.div(b) |
| | `<<` |
| | a.leftShift(b) |
| |
| | `%` |
| | a.mod(b) |
| | `>>` |
| | a.rightShift(b) |
| |
| | `**` |
| | a.power(b) |
| | `>>>` |
| | a.rightShiftUnsigned(b) |
| |
| | `\|` |
| | a.or(b) |
| | `++` |
| | a.next() |
| |
| | `&` |
| | a.and(b) |
| | `--` |
| | a.previous() |
| |
| | `^` |
| | a.xor(b) |
| | `+a` |
| | a.positive() |
| |
| | `as` |
| | a.asType(b) |
| | `-a` |
| | a.negative() |
| |
| | `a()` |
| | a.call() |
| | `~a` |
| | a.bitwiseNegate() |
| |==== |
| |