| ////////////////////////////////////////// |
| |
| 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 style and language feature guidelines for Java developers |
| |
| A Java developer embarking on a Groovy adventure will always have Java in mind, and will progressively learn Groovy, |
| one feature at a time, becoming more productive and writing more idiomatic Groovy code. |
| This document's purpose is to guide such a developer along the way, teaching some common Groovy syntax style, |
| new operators, and new features like closures, etc. |
| This guide is not complete and only serves as a quick intro and a base for further guideline sections |
| should you feel like contributing to the document and enhancing it. |
| |
| == No semicolons |
| |
| When coming from a C / C++ / C# / Java background, we're so used to semicolons, that we put them everywhere. |
| Even worse, Groovy supports 99% of Java's syntax, and sometimes, |
| it's so easy to paste some Java code into your Groovy programs, that you end up with tons of semicolons everywhere. |
| But... semicolons are optional in Groovy, you can omit them, and it's more idiomatic to remove them. |
| |
| == Return keyword optional |
| |
| In Groovy, the last expression evaluated in the body of a method can be returned without necessitating the `return` keyword. |
| Especially for short methods and for closures, it's nicer to omit it for brevity: |
| |
| [source,groovy] |
| ---- |
| String toString() { return "a server" } |
| String toString() { "a server" } |
| ---- |
| |
| But sometimes, this doesn't look too good when you're using a variable, and see it visually twice on two rows: |
| |
| [source,groovy] |
| ---- |
| def props() { |
| def m1 = [a: 1, b: 2] |
| m2 = m1.findAll { k, v -> v % 2 == 0 } |
| m2.c = 3 |
| m2 |
| } |
| ---- |
| |
| In such case, either putting a newline before the last expression, or explicitly using `return` may yield better readability. |
| |
| I, for myself, sometimes use the `return` keyword, sometimes not, it's often a matter of taste. |
| But often, inside of closure, we omit it more often than not, for example. So even if the keyword is optional, |
| this is by no means mandatory to not use it if you think it halters the readability of your code. |
| |
| A word of caution, however. When using methods which are defined with the `def` keyword instead of a specific concrete type, |
| you may be surprised to see the last expression being returned sometimes. So usually prefer using a specific return type like void or a type. |
| In our example above, imagine we forgot to put m2 as last statement to be returned, |
| the last expression would be `m2.c = 3`, which would return... `3`, and not the map you expect. |
| |
| Statements like `if`/`else`, `try`/`catch` can thus return a value as well, as there's a "last expression" evaluated in those statements: |
| |
| [source,groovy] |
| ---- |
| def foo(n) { |
| if(n == 1) { |
| "Roshan" |
| } else { |
| "Dawrani" |
| } |
| } |
| |
| assert foo(1) == "Roshan" |
| assert foo(2) == "Dawrani" |
| ---- |
| |
| == Def and type |
| |
| As we're talking about `def` and types, I often see developers using both `def` and a type. But `def` is redundant here. |
| So make a choice, either use `def` or a type. |
| |
| So don't write: |
| |
| [source,groovy] |
| ---- |
| def String name = "Guillaume" |
| ---- |
| |
| But: |
| |
| [source,groovy] |
| ---- |
| String name = "Guillaume" |
| ---- |
| |
| When using `def` in Groovy, the actual type holder is `Object` (so you can assign any object to variables defined with `def`, |
| and return any kind of object if a method is declared returning `def`). |
| |
| When defining a method with untyped parameters, you can use `def` but it's not needed, so we tend to omit them. |
| So instead of: |
| |
| [source,groovy] |
| ---- |
| void doSomething(def param1, def param2) { } |
| ---- |
| |
| Prefer: |
| |
| [source,groovy] |
| ---- |
| void doSomething(param1, param2) { } |
| ---- |
| |
| But as we mention in the last section of the document, it's usually better to type your method parameters, |
| so as to help with documenting your code, and also help IDEs for code-completion, |
| or for leveraging the static type checking or static compilation capabilities of Groovy. |
| |
| Another place where `def` is redundant and should be avoided is when defining constructors: |
| |
| [source,groovy] |
| ---- |
| class MyClass { |
| def MyClass() {} |
| } |
| ---- |
| |
| Instead, just remove the `def`: |
| |
| [source,groovy] |
| ---- |
| class MyClass { |
| MyClass() {} |
| } |
| ---- |
| |
| == Public by default |
| |
| By default, Groovy considers classes and methods `public`. |
| So you don't have to use the `public` modifier everywhere something is public. |
| Only if it's not public, you should put a visibility modifier. |
| |
| So instead of: |
| |
| [source,groovy] |
| ---- |
| public class Server { |
| public String toString() { return "a server" } |
| } |
| ---- |
| |
| Prefer the more concise: |
| |
| [source,groovy] |
| ---- |
| class Server { |
| String toString() { "a server" } |
| } |
| ---- |
| |
| You may wonder about the 'package-scope' visibility, |
| and the fact Groovy allows one to omit 'public' means that this scope is not supported by default, |
| but there's actually a special Groovy annotation which allows you to use that visibility: |
| |
| [source,groovy] |
| ---- |
| class Server { |
| @PackageScope Cluster cluster |
| } |
| ---- |
| |
| == Omitting parentheses |
| |
| Groovy allows you to omit the parentheses for top-level expressions, like with the `println` command: |
| |
| [source,groovy] |
| ---- |
| println "Hello" |
| method a, b |
| ---- |
| |
| vs: |
| |
| [source,groovy] |
| ---- |
| println("Hello") |
| method(a, b) |
| ---- |
| |
| When a closure is the last parameter of a method call, like when using Groovy's `each{}` iteration mechanism, |
| you can put the closure outside the closing parentheses, and even omit the parentheses: |
| |
| [source,groovy] |
| ---- |
| list.each( { println it } ) |
| list.each(){ println it } |
| list.each { println it } |
| ---- |
| |
| Always prefer the third form, which is more natural, as an empty pair of parentheses is just useless syntactical noise! |
| |
| In some cases parentheses are required, such as when making nested method calls or when calling a method without parameters. |
| |
| [source,groovy] |
| ---- |
| def foo(n) { n } |
| def bar() { 1 } |
| |
| println foo 1 // won't work |
| def m = bar // won't work |
| ---- |
| |
| == Classes as first-class citizens |
| |
| The `.class` suffix is not needed in Groovy, a bit like in Java's `instanceof`. |
| |
| For example: |
| |
| [source,groovy] |
| ---- |
| connection.doPost(BASE_URI + "/modify.hqu", params, ResourcesResponse.class) |
| ---- |
| |
| Using GStrings we're going to cover below, and using first class citizens: |
| |
| [source,groovy] |
| ---- |
| connection.doPost("${BASE_URI}/modify.hqu", params, ResourcesResponse) |
| ---- |
| |
| == Getters and Setters |
| |
| In Groovy, a getters and setters form what we call a "property", |
| and offers a shortcut notation for accessing and setting such properties. |
| So instead of the Java-way of calling getters / setters, you can use a field-like access notation: |
| |
| [source,groovy] |
| ---- |
| resourceGroup.getResourcePrototype().getName() == SERVER_TYPE_NAME |
| resourceGroup.resourcePrototype.name == SERVER_TYPE_NAME |
| |
| resourcePrototype.setName("something") |
| resourcePrototype.name = "something" |
| ---- |
| |
| When writing your beans in Groovy, often called POGOs (Plain Old Groovy Objects), |
| you don't have to create the field and getter / setter yourself, but let the Groovy compiler do it for you. |
| |
| So instead of: |
| |
| [source,groovy] |
| ---- |
| class Person { |
| private String name |
| String getName() { return name } |
| void setName(String name) { this.name = name } |
| } |
| ---- |
| |
| You can simply write: |
| |
| [source,groovy] |
| ---- |
| class Person { |
| String name |
| } |
| ---- |
| |
| As you can see, a free standing 'field' without modifier visibility actually |
| makes the Groovy compiler to generate a private field and a getter and setter for you. |
| |
| When using such POGOs from Java, the getter and setter are indeed there, and can be used as usual, of course. |
| |
| Although the compiler creates the usual getter/setter logic, |
| if you wish to do anything additional or different in those getters/setters, |
| you're free to still provide them, and the compiler will use your logic, instead of the default generated one. |
| |
| == Initializing beans with named parameters and the default constructor |
| |
| With a bean like: |
| |
| [source,groovy] |
| ---- |
| class Server { |
| String name |
| Cluster cluster |
| } |
| ---- |
| |
| Instead of setting each setter in subsequent statements as follows: |
| |
| [source,groovy] |
| ---- |
| def server = new Server() |
| server.name = "Obelix" |
| server.cluster = aCluster |
| ---- |
| |
| You can use named parameters with the default constructor (first the constructor is called, then the setters are called in the sequence in which they are specified in the map): |
| |
| [source,groovy] |
| ---- |
| def server = new Server(name: "Obelix", cluster: aCluster) |
| ---- |
| |
| == Using `with()` and `tap()` for repeated operations on the same bean |
| |
| Named-parameters with the default constructor is interesting when creating new instances, |
| but what if you are updating an instance that was given to you, do you have to repeat the 'server' prefix again and again? |
| No, thanks to the `with()` and `tap()` methods that Groovy adds on all objects of any kind: |
| |
| [source,groovy] |
| ---- |
| server.name = application.name |
| server.status = status |
| server.sessionCount = 3 |
| server.start() |
| server.stop() |
| ---- |
| |
| vs: |
| |
| [source,groovy] |
| ---- |
| server.with { |
| name = application.name |
| status = status |
| sessionCount = 3 |
| start() |
| stop() |
| } |
| ---- |
| |
| As with any closure in Groovy, the last statement is considered the return value. In the example above this is the result of `stop()`. To use this as a builder, that just returns the incoming object, there is also `tap()`: |
| |
| [source,groovy] |
| ---- |
| def person = new Person().with { |
| name = "Ada Lovelace" |
| it // Note the explicit mention of it as the return value |
| } |
| ---- |
| |
| vs: |
| |
| [source,groovy] |
| ---- |
| def person = new Person().tap { |
| name = "Ada Lovelace" |
| } |
| ---- |
| |
| Note: you can also use `with(true)` instead of `tap()` and `with(false)` instead of `with()`. |
| |
| == Equals and `==` |
| |
| Java's `==` is actually Groovy's `is()` method, and Groovy's `==` is a clever `equals()`! |
| |
| To compare the references of objects, instead of `==`, you should use `a.is(b)`. |
| |
| But to do the usual `equals()` comparison, you should prefer Groovy's `==`, |
| as it also takes care of avoiding `NullPointerException`, independently of whether the left or right is `null` or not. |
| |
| Instead of: |
| |
| [source,groovy] |
| ---- |
| status != null && status.equals(ControlConstants.STATUS_COMPLETED) |
| ---- |
| |
| Do: |
| |
| [source,groovy] |
| ---- |
| status == ControlConstants.STATUS_COMPLETED |
| ---- |
| |
| == GStrings (interpolation, multiline) |
| |
| We often use string and variable concatenation in Java, with many opening `/` closing of double quotes, plus signs, |
| and `\n` characters for newlines. |
| With interpolated strings (called GStrings), such strings look better and are less painful to type: |
| |
| [source,groovy] |
| ---- |
| throw new Exception("Unable to convert resource: " + resource) |
| ---- |
| |
| vs: |
| |
| [source,groovy] |
| ---- |
| throw new Exception("Unable to convert resource: ${resource}") |
| ---- |
| |
| Inside the curly braces, you can put any kind of expression, not just variables. |
| For simple variables, or `variable.property`, you can even drop the curly braces: |
| |
| [source,groovy] |
| ---- |
| throw new Exception("Unable to convert resource: $resource") |
| ---- |
| |
| You can even lazily evaluate those expressions using a closure notation with `${\-> resource }`. |
| When the GString will be coerced to a String, it'll evaluate the closure and get the `toString()` representation of the return value. |
| |
| Example: |
| |
| [source,groovy] |
| ---- |
| int i = 3 |
| |
| def s1 = "i's value is: ${i}" |
| def s2 = "i's value is: ${-> i}" |
| |
| i++ |
| |
| assert s1 == "i's value is: 3" // eagerly evaluated, takes the value on creation |
| assert s2 == "i's value is: 4" // lazily evaluated, takes the new value into account |
| ---- |
| |
| When strings and their concatenated expression are long in Java: |
| |
| [source,groovy] |
| ---- |
| throw new PluginException("Failed to execute command list-applications:" + |
| " The group with name " + |
| parameterMap.groupname[0] + |
| " is not compatible group of type " + |
| SERVER_TYPE_NAME) |
| ---- |
| |
| You can use the `\` continuation character (this is not a multiline string): |
| |
| [source,groovy] |
| ---- |
| throw new PluginException("Failed to execute command list-applications: \ |
| The group with name ${parameterMap.groupname[0]} \ |
| is not compatible group of type ${SERVER_TYPE_NAME}") |
| ---- |
| |
| Or using multiline strings with triple quotes: |
| |
| [source,groovy] |
| ---- |
| throw new PluginException("""Failed to execute command list-applications: |
| The group with name ${parameterMap.groupname[0]} |
| is not compatible group of type ${SERVER_TYPE_NAME)}""") |
| ---- |
| |
| You can also strip the indentation appearing on the left side of the multiline strings by calling `.stripIndent()` on that string. |
| |
| Also note the difference between single quotes and double quotes in Groovy: single quotes always create Java Strings, |
| without interpolation of variables, whereas double quotes either create Java Strings or GStrings when interpolated variables are present. |
| |
| For multiline strings, you can triple the quotes: i.e. triple double quotes for GStrings and triple single quotes for mere Strings. |
| |
| If you need to write regular expression patterns, you should use the "slashy" string notation: |
| |
| [source,groovy] |
| ---- |
| assert "foooo/baaaaar" ==~ /fo+\/ba+r/ |
| ---- |
| |
| The advantage of the "slashy" notation is that you don't need to double escape backslashes, making working with regex a bit simpler. |
| |
| Last but not least, prefer using single quoted strings when you need string constants, |
| and use double quoted strings when you are explicitly relying on string interpolation. |
| |
| == Native syntax for data structures |
| |
| Groovy provides native syntax constructs for data structures like lists, maps, regex, or ranges of values. |
| Make sure to leverage them in your Groovy programs. |
| |
| Here are some examples of those native constructs: |
| |
| [source,groovy] |
| ---- |
| include::{projectdir}/src/spec/test/StyleGuideTest.groovy[tags=data_structures,indent=0] |
| ---- |
| |
| == The Groovy Development Kit |
| |
| Continuing on the data structures, when you need to iterate over collections, |
| Groovy provides various additional methods, decorating Java's core data structures, |
| like `each{}`, `find{}`, `findAll{}`, `every{}`, `collect{}`, `inject{}`. |
| These methods add a functional flavor to the programming language and help working with complex algorithms more easily. |
| Lots of new methods are applied to various types, through decoration, thanks to the dynamic nature of the language. |
| You can find lots of very useful methods on String, Files, Streams, Collections, and much more: |
| |
| http://groovy-lang.org/gdk.html |
| |
| == The power of switch |
| |
| Groovy's `switch` is much more powerful than in C-ish languages which usually only accept primitives and assimilated. |
| Groovy's `switch` accepts pretty much any kind of type. |
| |
| [source,groovy] |
| ---- |
| def x = 1.23 |
| def result = "" |
| switch (x) { |
| case "foo": result = "found foo" |
| // lets fall through |
| case "bar": result += "bar" |
| case [4, 5, 6, 'inList']: |
| result = "list" |
| break |
| case 12..30: |
| result = "range" |
| break |
| case Integer: |
| result = "integer" |
| break |
| case Number: |
| result = "number" |
| break |
| case { it > 3 }: |
| result = "number > 3" |
| break |
| default: result = "default" |
| } |
| assert result == "number" |
| ---- |
| |
| And more generally, types with an `isCase()` method can also decide whether a value corresponds with a case |
| |
| == Import aliasing |
| |
| In Java, when using two classes of the same name but from different packages, like `java.util.List` |
| and `java.awt.List`, you can import one class, but have to use a fully-qualified name for the other. |
| |
| Also sometimes, in your code, multiple usages of a long class name, can increase verbosity and |
| reduce clarity of the code. |
| |
| To improve such situations, Groovy features import aliasing: |
| |
| [source,groovy] |
| ---- |
| import java.util.List as UtilList |
| import java.awt.List as AwtList |
| import javax.swing.WindowConstants as WC |
| |
| UtilList list1 = [WC.EXIT_ON_CLOSE] |
| assert list1.size() instanceof Integer |
| def list2 = new AwtList() |
| assert list2.size() instanceof java.awt.Dimension |
| ---- |
| |
| You can also use aliasing when importing methods statically: |
| |
| [source,groovy] |
| ---- |
| import static java.lang.Math.abs as mabs |
| assert mabs(-4) == 4 |
| ---- |
| |
| == Groovy Truth |
| |
| All objects can be 'coerced' to a boolean value: everything that's `null`, `void`, equal to zero, |
| or empty evaluates to `false`, and if not, evaluates to `true`. |
| |
| So instead of writing: |
| |
| [source,groovy] |
| ---- |
| if (name != null && name.length > 0) {} |
| ---- |
| |
| You can just do: |
| |
| [source,groovy] |
| ---- |
| if (name) {} |
| ---- |
| |
| Same thing for collections, etc. |
| |
| Thus, you can use some shortcuts in things like `while()`, `if()`, the ternary operator, the Elvis operator (see below), etc. |
| |
| It's even possible to customize the Groovy Truth, by adding an boolean `asBoolean()` method to your classes! |
| |
| == Safe graph navigation |
| |
| Groovy supports a variant of the `.` operator to safely navigate an object graph. |
| |
| In Java, when you're interested in a node deep in the graph and need to check for `null`, |
| you often end up writing complex `if`, or nested `if` statements like this: |
| |
| [source,groovy] |
| ---- |
| if (order != null) { |
| if (order.getCustomer() != null) { |
| if (order.getCustomer().getAddress() != null) { |
| System.out.println(order.getCustomer().getAddress()); |
| } |
| } |
| } |
| ---- |
| |
| With `?.` safe dereference operator, you can simplify such code with: |
| |
| [source,groovy] |
| ---- |
| println order?.customer?.address |
| ---- |
| |
| Nulls are checked throughout the call chain and no `NullPointerException` will be thrown if any element is `null`, |
| and the resulting value will be null if something's `null`. |
| |
| == Assert |
| |
| To check your parameters, your return values, and more, you can use the `assert` statement. |
| |
| Contrary to Java's `assert`, `assert`s don't need to be activated to be working, so `assert`s are always checked. |
| |
| [source,groovy] |
| ---- |
| def check(String name) { |
| // name non-null and non-empty according to Groovy Truth |
| assert name |
| // safe navigation + Groovy Truth to check |
| assert name?.size() > 3 |
| } |
| ---- |
| |
| You'll also notice the nice output that Groovy's "Power Assert" statement provides, |
| with a graph view of the various values of each sub-expressions being asserted. |
| |
| == Elvis operator for default values |
| |
| The Elvis operator is a special ternary operator shortcut which is handy to use for default values. |
| |
| We often have to write code like: |
| |
| [source,groovy] |
| ---- |
| def result = name != null ? name : "Unknown" |
| ---- |
| |
| Thanks to Groovy Truth, the `null` check can be simplified to just 'name'. |
| |
| And to go even further, since you return 'name' anyway, instead of repeating name twice in this ternary expression, |
| we can somehow remove what's in between the question mark and colon, by using the Elvis operator, so that the above becomes: |
| |
| [source,groovy] |
| ---- |
| def result = name ?: "Unknown" |
| ---- |
| |
| == Catch any exception |
| |
| If you don't really care about the type of the exception which is thrown inside your `try` block, |
| you can simply catch any of them and simply omit the type of the caught exception. |
| So instead of catching the exceptions like in: |
| |
| [source,groovy] |
| ---- |
| try { |
| // ... |
| } catch (Exception t) { |
| // something bad happens |
| } |
| ---- |
| |
| Then catch anything ('any' or 'all', or whatever makes you think it's anything): |
| |
| [source,groovy] |
| ---- |
| try { |
| // ... |
| } catch (any) { |
| // something bad happens |
| } |
| ---- |
| |
| [NOTE] |
| Note that it's catching all Exceptions, not `Throwable`s. If you need to really catch "everything", |
| you'll have to be explicit and say you want to catch `Throwable`s. |
| |
| == Optional typing advice |
| |
| I'll finish on some words on when and how to use optional typing. |
| Groovy lets you decide whether you use explicit strong typing, or when you use `def`. |
| |
| I've got a rather simple rule of thumb: whenever the code you're writing is going to be used by others as a public API, |
| you should always favor the use of strong typing, it helps making the contract stronger, |
| avoids possible passed arguments type mistakes, gives better documentation, and also helps the IDE with code completion. |
| Whenever the code is for your use only, like private methods, |
| or when the IDE can easily infer the type, then you're more free to decide when to type or not. |