Over a million developers have joined DZone.

Groovy Goodness: @DelegatesTo For Type Checking DSL

· Java Zone

Discover how AppDynamics steps in to upgrade your performance game and prevent your enterprise from these top 10 Java performance problems, brought to you in partnership with AppDynamics.

Groovy 2.1 introduced the @DelegatesTo annotation. With this annotation we can document a method and tell which class is responsible for executing the code we pass into the method. If we use @TypeChecked or @CompileStatic then the static type checker of the compiler will use this information to check at compile-time if the code is correct. And finally this annotation allows an IDE to give extra support like code completion.

Suppose we have the following class Reservation with the method submit(). The method accepts a closure with methods that need to be applied with the instance of the Reservation class. This is a very common pattern for writing simple DSLs in Groovy.

class Reservation {
    private Date date
    private String event
    private String attendee
    
    void date(final Date date) { this.date = date }
    void event(final String event) { this.event = event }
    void attendee(final String attendee) { this.attendee = attendee }
    
    /** Submit a reservation. 
      * @param config Configuration for reservation, invoking method class on Reservation.
      */
    static void submit(final Closure config) {
        final Reservation reservation = new Reservation()
        reservation.with config
    }

}

class Event {
    /** Use Reservation configuration DSL to submit a reservation. */
    void submitReservation() {
        Reservation.submit {
            date Date.parse('yyyyMMdd', '20130522')
            event 'Gr8Conf'
            attendee 'mrhaki'
            reserved true
        }
    }
}

final event = new Event()
event.submitReservation()

When we look at the code we might already see there is an error. In the Event.submitReservation() method we have the line reserved true, which will try to invoke the reserve() method of the Reservation class. But that method is not defined. When we run the application we get the expected error:

Exception thrown
May 28, 2013 6:58:36 AM org.codehaus.groovy.runtime.StackTraceUtils sanitize
WARNING: Sanitizing stacktrace:
groovy.lang.MissingMethodException: No signature of method: Reservation.reserved() is applicable for argument types: (java.lang.Boolean) values: [true]

To get an error for this line at compile-time we must add some annotation so the Groovy compiler can do static type checking on our code. We change the code and get the following sample:

import groovy.transform.*

class Reservation {
    private Date date
    private String event
    private String attendee
    
    void date(final Date date) { this.date = date }
    void event(final String event) { this.event = event }
    void attendee(final String attendee) { this.attendee = attendee }
    
    /** Submit a reservation. 
      * @param config Configuration for reservation, invoking method class on Reservation.
      */
    static void submit(@DelegatesTo(Reservation) final Closure config) {
        final Reservation reservation = new Reservation()
        reservation.with config
    }

}

@TypeChecked
// @CompileStatic - will also do static type checking
class Event {
    /** Use Reservation configuration DSL to submit a reservation. */
    void submitReservation() {
        Reservation.submit {
            date Date.parse('yyyyMMdd', '20130522')
            event 'Gr8Conf'
            attendee 'mrhaki'
            reserved true
        }
    }
}

final event = new Event()
event.submitReservation()

When the code is compiled we immediately get a compilation error:

1 compilation error:
 
[Static type checking] - Cannot find matching method Event#reserved(boolean). Please check if the declared type is right and if the method exists.
at line: 26, column: 13

Wow, this useful! We find errors in our DSL before the code is run.

When we create this code in an IDE like IntelliJ IDEA we also get code completion in the Reservation.submit() method invocation in theEvent class. The following screenshot shows code completion and a red font for reserved to indicate the compilation error.


Code written in Groovy 2.1.3

The Java Zone is brought to you in partnership with AppDynamics. AppDynamics helps you gain the fundamentals behind application performance, and implement best practices so you can proactively analyze and act on performance problems as they arise, and more specifically with your Java applications. Start a Free Trial.

Topics:

Published at DZone with permission of Hubert Klein Ikkink, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

The best of DZone straight to your inbox.

SEE AN EXAMPLE
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.
Subscribe

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

{{ parent.tldr }}

{{ parent.urlSource.name }}