Knowledge points about ellipsis in Groory language

Posted by byenary on Mon, 31 Jan 2022 17:43:55 +0100

[original: Style guide]

Style guide
Java developers who start exploring Groovy can never get rid of the Java way of thinking, which will gradually affect the learning of Groovy. The following features will guide you to improve development efficiency and write more authentic Groovy code. The purpose of this document is to guide such developers and teach some common Groovy syntax styles, new operators and new functions (such as closures). This guide is not complete and is only the basis for a quick introduction and further guide. Are you willing to contribute to and improve the document.

1. No semicolon

If you have experience in C / C++ / C# / Java language development, you must be used to semicolons, so we put them anywhere. Although Groovy supports 99% Java syntax, sometimes it's easy to paste some java code into Groovy programs, resulting in a large number of semicolons everywhere. But... Semicolons are optional in Groovy. We can omit them. Deleting semicolons is more authentic.

2. Return keyword optional

In Groovy, you can return the last expression evaluated in the method body without the return keyword. Especially for short methods and closures, it is best to omit them for brevity:

String toString() { return "a server" }
String toString() { "a server" }

But sometimes, when you use variables, this doesn't look good, and you see it intuitively twice on two lines:

def props() {
    def m1 = [a: 1, b: 2]
    m2 = m1.findAll { k, v -> v % 2 == 0 }
    m2.c = 3
    m2
}

In this case, placing a newline character before the last expression or explicitly using return may produce better readability.

As far as I'm concerned, sometimes I use the return keyword and sometimes I don't. this is usually a matter of taste. But, for example, inside a closure, we often omit it. Therefore, even if the keyword is optional, if you think it hinders the readability of the code, it is by no means mandatory not to use it.

But be careful. When using methods defined using the def keyword instead of a specific type, you may be surprised to see that sometimes the last expression is returned. Therefore, you usually prefer to use specific return types, such as void or type. In the above example, suppose we forget M2 as the last statement to return, and the last expression will be m2 C = 3, which will return... 3, not the result you expect.

Statements such as if/else and try/catch can also return a value because the "last expression" is evaluated in these statements:

def foo(n) {
    if(n == 1) {
        "Roshan"
    } else {
        "Dawrani"
    }
}

assert foo(1) == "Roshan"
assert foo(2) == "Dawrani"

3. Def and type

When we talk about def and types, I often see developers using both def and types. But DEF is redundant here. So make a choice, either use def or use type.

So don't write:

def String name = "Guillaume"

To write this:

String name = "Guillaume"
// Or write it like this
def name = "Guillaume"

When using def in Groovy, the actual type holder is Object (so you can assign any Object to a variable defined with def, and return any type of Object if the declaration method returns DEF).

When defining methods with untyped parameters, you can use def, but it is not required, so we tend to omit them. So instead of:

void doSomething(def param1, def param2) { }

It's better to write this:

void doSomething(param1, param2) { }

But as we mentioned in the last section of the document, it is usually best to enter method parameters to help record the code and help the IDE complete the code, or use Groovy's static type checking or static compilation function.

Another place where def is redundant and should be avoided is when defining constructors:

class MyClass {
    def MyClass() {}
}

Instead, simply delete def:

class MyClass {
    MyClass() {}
}

4. Default disclosure

By default, Groovy treats classes and methods as public. So you don't have to use the public modifier in any public place. You should place a visibility modifier only if it is not public.

So not:

public class Server {
    public String toString() { return "a server" }
}

Prefer more concise:

class Server {
    String toString() { "a server" }
}

You may want to know the visibility of package scope. The fact that Groovy allows omitting public means that this scope is not supported by default, but there is actually a special Groovy annotation that allows you to use this visibility:

class Server {
    @PackageScope Cluster cluster
}

5. Omit brackets

Groovy allows you to omit parentheses from top-level expressions, such as the println command:

println "Hello"
method a, b

contrast:

println("Hello")
method(a, b)

When the closure is the last parameter of the method call, for example, when using Groovy's each {} iteration, you can put the closure outside the right bracket or even omit the bracket:

list.each( { println it } )
list.each(){ println it }
list.each  { println it }

Always prefer the third form, which is more natural, because a pair of empty parentheses is just useless grammatical noise!

Parentheses are required in some cases, such as when making nested method calls or calling methods without parameters.

def foo(n) { n }
def bar() { 1 }

println foo 1 // won't work
def m = bar   // won't work

6. Classes as first-class citizens

Not required in Groovy class suffix, a bit like Java's instanceof.

For example:

connection.doPost(BASE_URI + "/modify.hqu", params, ResourcesResponse.class)

Using GStrings, we will introduce it below and use first-class citizens (?):

connection.doPost("${BASE_URI}/modify.hqu", params, ResourcesResponse)

7. Getters and Setters methods

In Groovy, getters and setters constitute what we call "properties" and provide shortcuts to access and set these properties. Therefore, you can use field like access notation instead of calling getter/setter in Java:

resourceGroup.getResourcePrototype().getName() == SERVER_TYPE_NAME
resourceGroup.resourcePrototype.name == SERVER_TYPE_NAME

resourcePrototype.setName("something")
resourcePrototype.name = "something"

When writing bean s in Groovy, they are usually called POGO (Plain Old Groovy Objects). You don't have to create fields and getters / setters yourself, but let the Groovy compiler do it for you.

So not:

class Person {
    private String name
    String getName() { return name }
    void setName(String name) { this.name = name }
}

You can simply write:

class Person {
    String name
}

As you can see, the compiler doesn't actually generate the visibility modifier "getter" and "Groovy" for private field s.

When using this kind of POGO in Java, getter s and setter s do exist. Of course, they can be used as usual.

Although the compiler creates the usual getter/setter logic, if you want to perform any other or different operations in these getters / setters, you are still free to provide them, and the compiler will use your logic instead of the default generated logic.

8. Initialize the bean with named parameters and default constructor

Beans like this:

class Server {
    String name
    Cluster cluster
}

Instead of setting each setter in subsequent statements, as shown below:

def server = new Server()
server.name = "Obelix"
server.cluster = aCluster

You can use named parameters with the default constructor (call the constructor first, and then call the setter in the order they specify in the mapping):

def server = new Server(name: "Obelix", cluster: aCluster)

9. Use with() and tap() to repeat 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:
When creating a new instance, the named parameters with the default constructor are interesting, but if you want to update the instance provided to you, do you have to repeat the "server" prefix again and again? No, thanks to Groovy's with() and tap() methods added to all objects of any type:

server.name = application.name
server.status = status
server.sessionCount = 3
server.start()
server.stop()

contrast:

server.with {
    name = application.name
    status = status
    sessionCount = 3
    start()
    stop()
}

Like any closure in Groovy, the last statement is treated as a return value. In the above example, this is the result of stop(). To use it as a builder that returns only incoming objects, there is also tap():

def person = new Person().with {
    name = "Ada Lovelace"
    it // Note the explicit mention of it as the return value
}

contrast:

def person = new Person().tap {
    name = "Ada Lovelace"
}

Note: you can also use with(true) instead of tap() and with(false) instead of with().

10. Equals and==

Java's = = is actually Groovy's is() method, while Groovy's = = is a smarter equals()!

To compare references to objects, you should use a.is(b) instead of = =.

But for the usual equals() comparison, you should prefer Groovy's = =, because it also avoids NullPointerException, regardless of whether left or right is null.

no

status != null && status.equals(ControlConstants.STATUS_COMPLETED)

Instead:

status == ControlConstants.STATUS_COMPLETED

11. GStrings (interpolation, multiline)

We often use string and variable concatenation in Java, in which there are many double quotes, plus signs and on / off characters as line breaks. Using interpolated strings (called gsstrings), such strings look better and are less painful to enter:

throw new Exception("Unable to convert resource: " + resource)

contrast:

throw new Exception("Unable to convert resource: ${resource}")

Within curly braces, you can place any type of expression, not just variables. For simple variables, or variable Property, you can even remove the curly braces:

throw new Exception("Unable to convert resource: $resource")

You can even lazily evaluate these expressions using closure notation with ${- > resource}. When GString is to be cast to a String, it evaluates the closure and gets a toString() representation of the return value.

example:

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 the string and its connected expression are very long in Java:

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 continue to splice characters using \ (this is not a multiline string):

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 use a multiline string with triple quotes:

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 call the string stripIndent() to remove the indent that appears to the left of the multiline string.

Also note the difference between single quotation marks and double quotation marks in Groovy: single quotation marks always create a Java string without inserting variables, while double quotation marks create a Java string or GString when there are inserted variables.

For multiline strings, you can use triple quotes: triple double quotes for GString and triple single quotes for plain strings.

If you need to write a regular expression pattern, you should use the "slash" string representation:

assert "foooo/baaaaar" ==~ /fo+\/ba+r/

The advantage of the slash symbol is that you don't need to double escape backslashes, making it easier to use regular expressions.

Last but not least, you prefer single quote strings when you need string constants and double quote strings when you explicitly rely on string interpolation.

12. Native syntax of data structure

Groovy provides native syntax constructs for data structures such as lists, maps, regex, or ranges. Make sure you use them in your groovy program.

Here are some examples of these native structures:

def list = [1, 4, 6, 9]

// by default, keys are Strings, no need to quote them
// you can wrap keys with () like [(variableStateAcronym): stateName] to insert a variable or object as a key.
def map = [CA: 'California', MI: 'Michigan']

// ranges can be inclusive and exclusive
def range = 10..20 // inclusive
assert range.size() == 11
// use brackets if you need to call a method on a range definition
assert (10..<20).size() == 10 // exclusive

def pattern = ~/fo*/

// equivalent to add()
list << 5

// call contains()
assert 4 in list
assert 5 in list
assert 15 in range

// subscript notation
assert list[1] == 4

// add a new key value pair
map << [WA: 'Washington']
// subscript notation
assert map['CA'] == 'California'
// property notation
assert map.WA == 'Washington'

// matches() strings against patterns
assert 'foo' ==~ pattern

13. Groovy development kit

Continue to introduce data structures. When you need to iterate collections, Groovy provides various additional methods to modify the core data structures of Java, such as each {}, find {}, findAll {}, every {}, collect {}, and inject {}. These methods add a functional style to the programming language and help you deal with complex algorithms more easily. Due to the dynamic nature of language, many new methods are applied to various types through decoration. You can find many useful methods in String, Files, Streams, Collections and so on:

http://groovy-lang.org/gdk.html

14. Super switch

Groovy's switch is much more powerful than the C language, which usually can only accept primitives and assimilation. Groovy's switch can accept almost any type.

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"

Generally speaking, types with isCase() methods can also determine whether a value corresponds to a case

15. Import alias

In Java, when using two classes with the same name but from different packages, such as java.util.List and java.awt.List, you can import one class, but you must use a fully qualified name for the other.

Sometimes, in your code, the repeated use of long class names will increase the verbosity of the code and reduce the clarity of the code.

To improve this situation, Groovy provides an import alias:

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 aliases when statically importing methods:

import static java.lang.Math.abs as mabs
assert mabs(-4) == 4

16. True and false of groovy

All objects can be "forced" to Boolean values: all objects that are null, void, equal to 0 or empty are evaluated as false, and if not, they are evaluated as true.

So instead of writing:

if (name != null && name.length > 0) {}

It can be written as follows:

if (name) {}

The same is true of collections and so on.

Therefore, you can use some shortcuts in while(), if(), ternary operator, Elvis operator (see below), etc.

You can even customize Groovy Truth by adding a Boolean asBoolean() method to your class!

17. Secure graphical navigation

Groovy support Operator to safely navigate the object graph.

In Java, when you are interested in a node in the diagram and need to check whether it is null, you usually end up writing complex if or nested if statements, as shown below:

if (order != null) {
    if (order.getCustomer() != null) {
        if (order.getCustomer().getAddress() != null) {
            System.out.println(order.getCustomer().getAddress());
        }
    }
}

Use Operators safely dereference operators. You can simplify such code by using the following methods:

println order?.customer?.address

Check the null value in the whole call chain. If any element is empty, NullPointerException will not be thrown. If an item is empty, the result value will be empty.

18. Assert

To check parameters, return values, and so on, you can use the assert statement.

In contrast to Java's assertions, assertions do not need to be activated to work, so always check for assertions.

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 that Groovy's "Power Assert" statement provides good output and a graphical view of the various values of each asserted subexpression.

19. Elvis operator of default value

Elvis operator is a special ternary operator shortcut that can be easily used for default values.

We often have to write code like this:

def result = name != null ? name : "Unknown"

Thanks to Groovy's true and false judgment, the null check can be simplified to "name".

Further, because you will return 'name' anyway instead of repeating name twice in this ternary expression, we can delete the content between the question mark and colon in some way by using Elvis operator, so the above content becomes:

def result = name ?: "Unknown"

20. Catch any exceptions

If you really don't care about the types of exceptions thrown in your try block, you can simply catch any of them and simply ignore the types of exceptions caught. So instead of catching exceptions like this:

try {
    // ...
} catch (Exception t) {
    // something bad happens
}

Then capture anything ('any 'or' all ', or anything that makes you think it's anything):

try {
    // ...
} catch (any) {
    // something bad happens
}

Note that it is catching all exceptions, not Throwable. If you really need to capture "everything", you must be clear and say that you want to capture Throwables.

21. Recommendations

I will complete some words about when and how to use optional input. Groovy lets you decide whether to use explicit strong typing or when to use def.

I have a fairly simple rule of thumb: whenever the code you write will be used by others as a public API, you should always support the use of strong typing, which helps to make the contract more powerful, avoid possible parameter type errors, provide better documentation, and help the IDE complete the code. Whenever the code is for your use only, such as private methods, or when the IDE can easily infer types, you have more freedom to decide when to type or not.

Topics: Android Groovy