Over a million developers have joined DZone.

Higher-order functions with Groovy, part 2

DZone's Guide to

Higher-order functions with Groovy, part 2

· Java Zone
Free Resource

Microservices! They are everywhere, or at least, the term is. When should you use a microservice architecture? What factors should be considered when making that decision? Do the benefits outweigh the costs? Why is everyone so excited about them, anyway?  Brought to you in partnership with IBM.

In part one I gave an overview of closures and currying, two features in Groovy that implement higher-order functions. Higher-order functions can be used in combination with regular Java classes. Java methods can be converted to closures and closures can be converted to Java methods.

To convert a Java method to a closure, place the ampersand sign (&) in front of the method name:

def isNotBlank = org.apache.commons.lang.StringUtils.&isNotBlank
assert isNotBlank instanceof Closure
assert isNotBlank("   text    ")

The inverse, to convert a closure to a Java method is equally easy. There is one restriction: you can only convert closures to method on interfaces. As an example I used the Specification pattern from the Domain-Driven Design book:

interface StringSpecification {
    boolean isSpecifiedBy(String s)

Now I can convert a closure to the isSpecifiedBy() method:

StringSpecification notBlankSpec =
    org.apache.commons.lang.StringUtils.&isNotBlank as StringSpecification

assert notBlankSpec.isSpecifiedBy("   text    ")

The as keyword is commonly used in Groovy to convert objects from one type to another. You can use the as keyword on closures to turn them into interface implementations. Groovy uses the java.lang.reflect.Proxy class for this.

One word of caution though. You can turn a closure into any interface, also interfaces that define multiple methods. Groovy will not complain. Here's an example:

Map m = org.apache.commons.lang.StringUtils.&isNotBlank as Map

assert m."   text     " == true // this calls the closure

If however you would call the put() method on java.util.Map you would get an error:

m.test = new Object()

Results in:

No signature of method: org.apache.commons.lang.StringUtils.isNotBlank() is applicable for argument types: (java.lang.String, java.lang.Object) values: {"test", java.lang.Object@1c783c5}

You can assign a specific closure to each method in an interface. Here's an example:

interface Visitor {
    void onSuccess(Object o)
    void onFailure(Throwable t)

Visitor v = [
    onSuccess: { println it },
    onFailure: { it.printStackTrace() }
] as Visitor

Here the as keyword is called on a Map object, converting it to an instance of the Visitor interface. This approach works well as long as methods are not overloaded. You can only assign one closure per method name. One closure would thus be called for all overloaded methods.

Since I'm working with closures I can convert Java methods into closures and curry:

import org.apache.log4j.*

Logger logger = Logger.getLogger(this.getClass())

Visitor v = [
    onSuccess: logger.&info,
    onFailure: {
        Closure _delegate, Throwable t ->

] as Visitor

v.onFailure(new RuntimeException(new NullPointerException()))

The call to the curry() method on line 11 passes the getRootCause() method as closure to the declared closure as an argument. This results in a closure that accepts a Throwable argument which is compatible with the onFailure() method on the Visitor interface.

The calls to the onSuccess() and onFailure() methods produces this output:

INFO - Yeah!
WARN - java.lang.NullPointerException

In the next and final installment I'll discuss how calls to methods and properties are handled inside closures.

Update: part three has been posted too.

Happy coding!

Discover how the Watson team is further developing SDKs in Java, Node.js, Python, iOS, and Android to access these services and make programming easy. Brought to you in partnership with IBM.


Opinions expressed by DZone contributors are their own.

The best of DZone straight to your inbox.

Please provide a valid email address.

Thanks for subscribing!

Awesome! Check your inbox to verify your email so you can start receiving the latest in tech news and resources.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}