Search

Dark theme | Light theme
Showing posts with label GroovyGoodness:Syntax. Show all posts
Showing posts with label GroovyGoodness:Syntax. Show all posts

June 28, 2022

Groovy Goodness: Using The Switch Expression

In a previous (old) post we learned how Groovy supports different classifiers for a switch case statement. Since Groovy 4 we can use switch also as an expression. This means the switch statement returns a value without having to use return. Instead of using a colon (:) and break we use the -> notation for a case. We specify the value that the switch expressions returns after ->. When we need a code block we simply put the code between curly braces ({...}).

In the following example we use the switch expression with different case statements:

def testSwitch(val) {
    switch (val) {
        case 52 -> 'Number value match'
        case "Groovy 4" -> 'String value match'
        case ~/^Switch.*Groovy$/ -> 'Pattern match'
        case BigInteger -> 'Class isInstance'
        case 60..90 -> 'Range contains'
        case [21, 'test', 9.12] -> 'List contains'
        case 42.056 -> 'Object equals'
        case { it instanceof Integer && it < 50 } -> 'Closure boolean'
        case [groovy: 'Rocks!', version: '1.7.6'] -> "Map contains key '$val'"
        default -> 'Default'
    } 
}

assert testSwitch(52) == 'Number value match'
assert testSwitch("Groovy 4") == 'String value match'
assert testSwitch("Switch to Groovy") == 'Pattern match'
assert testSwitch(42G) == 'Class isInstance'
assert testSwitch(70) == 'Range contains'
assert testSwitch('test') == 'List contains'
assert testSwitch(42.056) == 'Object equals'
assert testSwitch(20) == 'Closure boolean' 
assert testSwitch('groovy') == "Map contains key 'groovy'"
assert testSwitch('default') == 'Default'

Written with Groovy 4.0.3.

October 13, 2016

Groovy Goodness: Direct Field Access In (Super) Classes

When we use the property syntax of Groovy to get the value for a property, Groovy will actually try to invoke a get method for that property if it is available. So for example if we have the statement user.name actually user.getName() is invoked. If we want to reference a property field directly, so bypassing the get method, we must place an @ in front of the property field name. In the previous example we would write user.@name to get the field value directly. The same rules apply for setting a value for a property with the Groovy syntax. If we write user.name = 'mrhaki' then actually user.setName('mrhaki') is invoked. We can use the @ prefix also to set a value without invoking the set method for that property. So in our example it would be user.@name = 'mrhaki' and the setName method is not used.

In the following example we have a class Person with a name property. We add a getName method which formats the name field and returns the value. In a subclass User we access the name property from the super class using the Groovy property syntax and with the @ prefix:

class Person {
    // We must not leave out a modifier,
    // because without a modifier, 
    // Groovy would add
    // a getName and setName method if
    // they are not already available.
    // But Groovy also adds a private modifier
    // for this property and being
    // private it is not accessible from
    // subclasses.
    protected String name

    String getName() {
        "_${name}_"
    }

    void setName(String name) {
        this.name = "*${name}*"
    }
}

class User extends Person {
    String getUsername() {
        // .name will invoke getName().
        "User(${this.name})"
    }

    String getUsernameField() {
        // .@name will access name field.
        "User(${this.@name})"
    }
}

def u = new User(name: 'mrhaki')

assert u.username == 'User(_*mrhaki*_)'
assert u.usernameField == 'User(*mrhaki*)'
assert u.name == '_*mrhaki*_'
assert u.@name == '*mrhaki*'

// Set field value directly
u.@name = 'mrhaki'

assert u.username == 'User(_mrhaki_)'
assert u.usernameField == 'User(mrhaki)'
assert u.name == '_mrhaki_'
assert u.@name == 'mrhaki'

Written with Groovy 2.4.7.

September 22, 2015

Groovy Goodness: Defining Public Accessible Constant Fields

There is a catch when we define a constant field in Groovy. Rob Fletcher blogged about this in the post Groovy the public keyword a while ago. When we omit the public keyword for a method then the method is still accessible as public method, because Groovy makes the method public when the class is compiled. When we leave out the public keyword for fields Groovy creates a getter and setter method for the field at compile time and turns it into a property that applies to the Java Bean specification. This is also true if the field is static. So if we define a constant value as static final we must keep in mind that Groovy will generate a getter method so the constant value is a read only property according to Java Bean specification.

Let's create a simple class with a constant field DEFAULT, a property message and a message method. We leave out any public keyword:

// File: sample.groovy
// Groovy makes class public.
class Sample {
    // Groovy adds getDEFAULT and no setDEFAULT.
    static final String DEFAULT = 'default'
  
    // Groovy adds setMessage/getMessage
    String message

    // Groovy makes method public.
    void message(final String newMessage) {
        this.message = message
    }
}

If we compile this class we get the following methods and fields (using javap to inspect the class):

$ javap -p -constants Sample
Compiled from "sample.groovy"
public class Sample implements groovy.lang.GroovyObject {
  private static final java.lang.String DEFAULT = "default";
  private java.lang.String message;
  ...
  public void message(java.lang.String);
  ...
  public static final java.lang.String getDEFAULT();
  public java.lang.String getMessage();
  public void setMessage(java.lang.String);
}

If we want to access the constant field in Groovy we can still use Sample.DEFAULT, but from Java code this doesn't work. You can see in the generated class file we should invoke getDEFAULT(), because this method is public. To overcome this we simply add public to our constant field definition. This way Groovy will leave the field unchanged and in the generated class file it is still public. Then from Java we can use Sample.DEFAULT to access the constant value. Let's see the output of javap when we make the DEFAULT field public:

$ javap -p -constants Sample
Compiled from "sample.groovy"
public class Sample implements groovy.lang.GroovyObject {
  public static final java.lang.String DEFAULT = "default";
  private java.lang.String message;
  ...
  public void message(java.lang.String);
  ...
  public java.lang.String getMessage();
  public void setMessage(java.lang.String);
}

This also helps an IDE, like IntelliJ IDEA, to do a proper import static based on the constant field.

Written with Groovy 2.4.4.

September 11, 2015

Groovy Goodness: Operator Overloading in Reverse

One of the very nice features of Groovy is that we can implement operator overloading. This blog post is not about how to implement operator overloading, but Groovy's operator overloading also means that operators we know in Java have corresponding methods, which are not available in Java. So instead of using operators in our code we can use the corresponding methods.

The following sample code shows some operators we know in Java and the corresponding methods in Groovy:

def a = true
def b = false

assert a | b
// Java Boolean has no or method.
assert a.or(b) 

assert !(a & b)
assert !(a.and(b))


def x = 100
def y = 10

assert x + y == 110
// Java Integer has no plus method.
assert x.plus(y) == 110 

assert ++x == 101  
// ++ maps to next method.
assert x.next() == 102

assert --y == 9
assert y.previous() == 8

Written with Groovy 2.4.4.

May 22, 2014

Groovy Goodness: Implementing Traits at Runtime

Groovy 2.3 introduced traits as a new language construct. We can apply traits when we write new classes in Groovy using the implements keyword. But we can also apply traits dynamically at runtime. For example if we want to apply a trait to an already existing class and we cannot change the source code of that class.

To apply a single trait we can use the as keyword. The original object is then coerced into the trait instance. In the following sample we define a new trait Name with a couple of properties and a method. Also we have a User class we want to apply this trait to. After we have applied the trait we can access the properties of the Name trait and invoke the method getFullName, actually we get a new object instance with the capabilities of the User class and Name trait:

trait Name {
    String salutation, firstName, lastName

    String getFullName() {
        [salutation, firstName, lastName].join(' ')
    }
}

class User {
    String username, password   
}

def user = new User() as Name
user.with {
    salutation = 'Mr.'
    firstName = 'Hubert'
    lastName = 'Klein Ikkink'
    username = 'mrhaki'
    password = '*****'
}

assert user.fullName == 'Mr. Hubert Klein Ikkink'
assert user.username == 'mrhaki'

To apply multiple traits to an object we must use the method withTraits on an object. We can use one or more traits as arguments to this method. In the following sample we apply the traits Id, Version and Active with some properties and methods to an instance of the Person class. The withTraits methods return a new object instance which has all the properties and methods of the original class and the traits.

trait Id {
    Long id
}

trait Version {
    Long version = 0
}

trait Active {
    Date from = new Date()
    Date to = null

    boolean isActive() {
        final Date now = new Date()
        from < now && (!to || to > now)
    }
}

class Person {
    String username
}

def person = new Person(username: 'mrhaki')
def domainPerson = person.withTraits Id, Version, Active

domainPerson.id = 1

assert domainPerson.username == 'mrhaki'
assert domainPerson.id == 1
assert domainPerson.version == 0
assert domainPerson.active

Code written with Groovy 2.3.1.

Groovy Goodness: Chaining Traits

Since Groovy 2.3 we can use traits in our code. We can even chain traits using the so called stackable traits feature. This means a trait can delegate to another trait or we can stop the delegation base based on a condition. Inside the method implementation of a trait we can use super.method to delegate to the next trait (if available) in the chain. If we don't use super.method the chain is stopped.

In the following sample code we want to transform a String input value to an output value. We implement the transform method from the Transformer interface using different traits. For example the Upper trait will make the input value upper cased and invokes super.transform to delegate to the next trait. Then we write new classes that implement the traits. The order of the traits determines the chain. The last declared trait is invoked first then the traits defined from right to left are invoked.

/** 
 * Simple interface with one method to 
 * transform a String value.
 */
interface Transformer {
    String transform(String input)
}

/** Default trait will return the input value unchanged. */
trait DefaultTransformer implements Transformer {
    String transform(String input) {
        input
    }
}

/** Transform the String value to upper case */
trait Upper implements Transformer {
    String transform(String input) {
        super.transform(input.toUpperCase())
    }
}

/** Remove 'mr' from input String value. */
trait Filter implements Transformer {
    String transform(String input) {
        super.transform(input - 'mr')
    }
}

/**
 * Simple class uses three traits. The value property get method
 * returns the transformed value.
 */
class StringTransformer implements DefaultTransformer, Upper, Filter {
    String value
    String getValue() { transform(value) }
}

// Create StringTransformer instance.
def transformer = new StringTransformer(value: 'mrhaki')

assert transformer.value == 'HAKI' 


// Use same traits, but in different order.
class OtherStringTransformer implements DefaultTransformer, Filter, Upper {
    String value
    String getValue() { transform(value) }
}

// Create OtherStringTransformer instance.
def otherTransformer = new OtherStringTransformer(value: 'mrhaki')

// The Filter trait cannot find 'mr', 
// because the String value is already in
// upper case after the Upper trait.
assert otherTransformer.value == 'MRHAKI'


/** Only chain input values smaller than 5 characters. */
trait SmallFilter implements Transformer {
    String transform(String input) {
        if (input.size() < 5) {
            super.transform(input)
        } else {
            ''
        }
    }
}

class SmallStringTransformer implements DefaultTransformer, Upper, SmallFilter {
    String value
    String getValue() { transform(value) }
}

def smallTransformer = new SmallStringTransformer(value: 'mrhaki')
assert smallTransformer.value == ''

smallTransformer.value = 'haki'
assert smallTransformer.value == 'HAKI'

Code written with Groovy 2.3.1.

October 3, 2012

Groovy Goodness: Using Project Coin Features Also With Older Java Versions

Since Groovy 2 we can use a subset of the Project Coin features from Java 7. But we don't have to run Java 7 to use them in Groovy code. We can use the new features even if we run our Groovy code on older Java versions.

Groovy didn't have to add all Project Coin features, because some are already supported in Groovy, like the switch statement on String objects or diamond operator. A feature that is added is a syntax enhancement to define binary literals. We can now use binary integral literals by prefixing the value with 0b:

// Binary notation.
int x = 0b101
assert x == 5

The underscore in number literals is now supported in Groovy. We can define numbers and use an underscore to make them more readable. The value is not changed:

import static java.text.NumberFormat.getInstance as formatter
import static java.util.Locale.US

// Use underscore for number literals.
double d = 89_192.29
assert formatter(US).format(d) == '89,192.29'

long longNumber = 1230_3910_1929_182931
assert longNumber == 123039101929182931

int length = 5_10
assert length == 510

long hex = 0x00_ff
assert hex == 255

We can define a multi-catch exception in Groovy 2. We specify more than exception in the catch clause separated by a pipe (|) symbol:

import java.lang.reflect.*

// Multicatch.
@groovy.transform.ToString
class Person {
    String name
}

try {
    final Person p = new Person(name: 'mrhaki')
    final Method toString = p.class.getMethod("toString1", null)
    final Object result = toString.invoke(p, null)
} catch (NoSuchMethodException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
    assert e
}

(Code written with Groovy 2.0.4)

January 17, 2010

Groovy Goodness: Create Elements in Collection Definition

Small things can make our lives easier. For example if we use Groovy we can create a new list or map with a list of elements directly. We don't have to use an add() method.

def list = [
    'one', 
    'two', 
    'three',
]

def map = [
    'groovy': 1.7,
    'mrhaki': 'hubert',
]

assert list
assert map

December 8, 2009

Groovy Goodness: Semicolons are Optional

In Groovy we can leave out semicolons at the end of the line. As long as we use one statement per line. If we use multiple statements on a line we must use a semicolon to separate the statements.

assert true
assert !false

assert true; assert !false  // Need semicolon to separate statements.

assert true;  // Optional, so we can use them at the end of the line.
assert !false;

November 12, 2009

Groovy Goodness: Passing Closures to Methods

Closures are blocks of code we can assign to variables and pass around like objects. We can use closures as method arguments, but we must make sure we use the correct syntax. Groovy has some variations we can use to pass a closure into a method. If for example the closure is the last argument for a method we can put the closure outside the argument list.

// Method with two arguments. Last argument is a closure.
def work(input, cl) {
    cl(input)
}

// Define a closure.
def assertJava = {
    it == 'Java'
}

work('Java', assertJava)

work 'Java', assertJava  // No parenthesis.

work('Groovy', {
    assert it == 'Groovy' 
})  // Anonymous closure as argument.

work('Groovy') {
    assert it == 'Groovy'
}  // Last argument is closure and can be outside parenthesis.

work('Groovy')
{
     assert it == 'Groovy'
}  // Opening bracket on new line. If we want a code block (e.g. static initializer) instead of closure we must use ; to separate code.


work 'Groovy', {
    assert it == 'Groovy'
}  // Pay attention, no parenthesis, so comma is needed again!


// Does not work:
//
// Comma between argument list needed:
// work 'Groovy' {
//     assert it == 'Groovy'
// }

November 10, 2009

Groovy Goodness: Define Your Own Type Conversion

In Groovy we can override the method asType() to convert an object into another type. We can use the method asType() in our code to invoke the conversion, but we can even make it shorter an use as.

class Size {
    def x, y
    
    Object asType(Class clazz) {
        if (clazz == SquaredSize) {
            new SquaredSize(x: x**2, y: y**2)
        }
    }
}

class SquaredSize {
    def x, y
    
    String toString() { "x: $x, y: $y" }
}

def size = new Size(x: 10, y: 5)
def squared = size as SquaredSize  // Or size.asType(SquaredSize)

println squared  // Output: x: 100, y: 25

assert 100 == squared.x
assert 25 == squared.y

October 24, 2009

Groovy Goodness: Optional Parenthesis

Groovy can make code more readable with small features like optional parenthesis. If a method contains at least one argument we can leave out the parenthesis. There must also be no ambiquity, before we can leave out the parenthesis, otherwise the results can be unexpected. This also works for methods with named parameters and as a result the code could already look like a more natural language.

class User {
    String name    
    def connections = []
    
    def speaks(args) {
        connections.find{it.name == args.to.name}.listen(args.saying)
    }
    
    def meets(user) {
        connections << user
    }
    
    def listen(text) {
        println "I, $name, am listening to '$text'"
    }

    def greeting() {
        "Hi, I am $name."
    }
}

def mrhaki = new User(name: 'mrhaki')
def hubert = new User(name: 'hubert')

mrhaki.meets hubert  // No parenthesis needed.

println mrhaki.greeting()  // Need to use parenthesis for greeting, otherwise Groovy thinks greeting is a property.
// Output: Hi, I am mrhaki

mrhaki.speaks to: hubert, saying: 'How are you doing?'  // Named parameters.
// Output: I, hubert, am listening to 'How are you doing?'

// Look out with closures as the last element.
// The method can be invoked in several ways.
def list = [1,2,3,4]
list.sum(100, { it * 2 })  // Traditional, all parenthesis and comma.
list.sum(100) { it * 2 }  // Last argument is closure, so can be outside parenthesis and no comma 
list.sum 100, { it * 2 }  // No parenthesis, but with the comma.

October 23, 2009

Groovy Goodness: Static Imports

Java already has static imports, but Groovy takes it to the next level. In a previous post we learned how we use the as keyword to assign a class we import to a variable in the import statement. But with static imports we can do this even for static methods and properties. This makes for code which may confuse us, but if we look at the import section all will be revealed.

import static HttpURLConnection.HTTP_OK  // Normal Java static import.
import static HttpURLConnection.HTTP_OK as okay 
import static HttpURLConnection.setFollowRedirects as redirect
import java.net.HttpURLConnection as http // Non static import.

redirect false  // HttpURLConnection.setFollowRedirects(false)
assert false == HttpURLConnection.followRedirects

def c = (http) 'http://mrhaki.blogspot.com'.toURL().openConnection()
assert c instanceof HttpURLConnection

assert okay == c.responseCode
assert HTTP_OK == c.responseCode

October 19, 2009

Groovy Goodness: BigDecimal

In Groovy java.math.BigDecimal is the default type for non-integer floating types. Because of default imports we don't have to import the BigDecimal class, but we can use it right away. To create a BigDecimal (or BigInteger) we can use the "G" or "g" suffix.

assert 123g instanceof BigInteger
assert 42G instanceof BigInteger
assert 1.423 instanceof BigDecimal
assert 42.0g instanceof BigDecimal
assert 9203.10291G instanceof BigDecimal

// Let's do some math.
assert 3.2 == 1.2G + 2G
assert 10 == 10.1g - 0.1G

October 16, 2009

Groovy Goodness: Navigating with GPath

Groovy has GPath, a path expression language to navigate in XML or POJOs. We can use a simple dot-notation to identify elements in XML or a set of POJOs. This results in clean and dense code.

def xml = '''
<languages>
    <language id="1" jvm="true">Groovy</language>
    <language id="2" jvm="true">Java</language>
    <language id="3" jvm="false">Ruby</language>
</languages>
'''

def languages = new XmlSlurper().parseText(xml)
// Navigate in XML with GPath.
assert 3 == languages.language.size()
assert 'Groovy' == languages.language.find { it.'@id' == 1 }.text()
assert ['Groovy', 'Java', 'Ruby'] == languages.language.collect { it.text() }
assert 1 == languages.language.find { it == /Groovy/ }['@id'].toInteger()

// Navigating with GPath through object graph.
assert 75 == String.metaClass.methods.name.size()
assert ['copyValueOf', 'format', 'valueOf'] == String.metaClass.methods.findAll { it.static }.name.unique()
assert ['replace', 'replace', 'replaceAll', 'replaceFirst'] == String.metaClass.methods.name.grep(~/replace.*/)
assert ['class', 'bytes', 'empty'] == String.metaClass.properties.name
assert ['java.lang.Class', 'byte[]', 'boolean'] == String.metaClass.properties.type.canonicalName

October 15, 2009

Groovy Goodness: Default Imports

Groovy allows to write dense code. For example we don't have to write explicit import statements for a lot of every day packages. The following packages are imported by default in Groovy:

java.io.* 
java.lang.* 
java.math.BigDecimal 
java.math.BigInteger 
java.net.* 
java.util.* 
groovy.lang.* 
groovy.util.* 

So we can write code like this without an import statement:

def now = new Date()
def file = new File(".")
def url = new URL('http://mrhaki.blogspot.com')
def list = new ArrayList()

October 13, 2009

Groovy Goodness: Using AntBuilder

Groovy 1.6.5 includes Ant 1.7.1 and we can use Ant from our Groovy code with the AntBuilder class. The AntBuilder provides a nice DSL to create Ant tasks and to execute them. The following piece of code shows some samples of what we can do with the AntBuilder:

// Create AntBuilder.
def ant = new AntBuilder()

// Simple echo task.
def world = 'Groovy'
ant.echo("Hello $world")  // Output: [echo] Hello Groovy

// Ant task properties are defined as a map.
def options = [src: 'http://mrhaki.blogspot.com', dest: 'blog.html']
ant.get(options)
// is the same as
ant.get(src: 'http://mrhaki.blogspot.com', dest: 'blog.html']

// Nice builder syntax for tasks with for example filesets.
def zipfile = 'test.zip'
def current = '.'
ant.zip(destfile: zipfile) {
    fileset(dir: current) {
        include(name: '**/*.txt')
    }
}

September 15, 2009

Groovy Goodness: Check for Object Equality

Groovy overloads the == operator and maps it to the equals() method. This is very different from Java, so when developers are switching back and forth between Groovy and Java mistakes are bound to happen. In Java we use the == operator to see if variables are referring to the same object instance. In Groovy we use the == operator to see if two objects are the same, in Java we would use the equals() method for this. To test if two variables are referring to the same object instance in Groovy we use the is() method. The != operator is also overloaded and maps to the !equals() statement.

And because we are in Groovy land all null values are handled gracefully. We don't have to write extra checks to check for null values before we can test for equality.

Integer myInt = 42
Integer anotherInt = myInt
Integer newInt = 42
Integer different = 101

assert myInt == anotherInt  // In Java: myInt != null && myInt.equals(anotherInt)
assert myInt.is(anotherInt)  // In Java: myInt == anotherInt

assert myInt == newInt

assert myInt != different

September 14, 2009

Groovy Goodness: Named Parameters are Converted to Map

We can use named parameters when we invoke a Groovy method, but Groovy doesn't invoke the method with just those parameters. Groovy collects all named parameters and puts them in a Map. The Map is passed on to the method as the first argument. The method needs to know how to get the information from the Map and process it. There is one rule: the first parameter of the method must be the Map parameter, because Groovy will put the Map as first argument in the method invocation. Other parameters are appended in the same order as defined in the method invocation.

class Address {
    String street, city
    int number    
}

class Person {
    String name
    Address address
    String phoneNumber
    
    def move(newAddress, newPhoneNumber) {
        address.street = newAddress.street
        address.number = newAddress['number']
        address.city   = newAddress."city"
        phoneNumber = newPhoneNumber
    }
}

def a = new Address(street: 'Main St.', number: 1, city: 'Duck City')
def p = new Person(name: 'mrhaki', address: a, phoneNumber: '555-123499102')

p.move(street: 'High Av.', city: 'New Yark', '00920120', number: 42)  
// Groovy transform the method invocation to:
// p.move([street: 'High Av.', number: 42, city: 'New Yark'], '555-00920120')

assert 'High Av.' == p.address.street
assert 42 == p.address.number
assert 'New Yark' == p.address.city
assert '555-00920120' == p.phoneNumber
assert 'mrhaki' == p.name

September 12, 2009

Groovy Goodness: Parameters with Default Values

In Groovy we can assign default values to parameters in a method. If we define a default value for a parameter Groovy basically supports two method signatures: one with all parameters and one where the parameter with a default value is omitted. If we use multiple parameters with default values then the right most parameter with a default value is first eliminated then the next and so on.

def say(msg = 'Hello', name = 'world') {
    "$msg $name!"
}

// We can invoke 3 signatures:
// say(), say(msg), say(msg, name)
assert 'Hello world!' == say()
// Right most parameter with default value is eliminated first.
assert 'Hi world!' == say('Hi')
assert 'Howdy, mrhaki!' == say('Howdy,', 'mrhaki')

Run this script in GroovyConsole.