Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

Composition and Delegation in Groovy

DZone's Guide to

Composition and Delegation in Groovy

Using composition with Groovy? Then it's a good idea to learn the specifics of the @Delegate annotation to see where it does, and doesn't, work when generating code.

· Java Zone ·
Free Resource

How do you break a Monolith into Microservices at Scale? This ebook shows strategies and techniques for building scalable and resilient microservices.

When dealing with OOP languages, composition over inheritance has been the preferred approach for a while now, as it offers a greater flexibility in most cases. Before you start, I’m not going to get into that dispute whether one should opt for inheritance or not. It's enough to say that there are cases where composition is used. And that comes with its own headaches!

One of the major headaches, especially when dealing with languages such as Java, is adding delegation into the picture, as it requires a lot of code to be written (duplicated) in the host class just to be able to delegate to the class you are enclosing. (This is the point where a lot of the inheritance supporters would just smirk and remind me you don’t have to do that when you are using inheritance. Let’s just ignore them for now!)

There are some IDEs that provide tools for generating code for this, but typically, they do a one-off job: They generate all the methods and properties once and then, if you change the class after that, it’s down to you to take care and cascade any changes into the other class.

Groovy offers an awesome component to help with this: the @Delegate annotation. Simply annotate the member variable with this, and Groovy will generate all the getters/setters and methods in the owner class. Even more so, it actually modifies the owner class to implement all the interfaces that the field implements and provides implementation for these interfaces in the owner class, which simply just delegate the calls to the field.

What’s even nicer about this is that you can have multiple fields involved in delegation. In other words, you can create a composite class with a few fields and annotate each one of them with this and, despite not writing any other code, your class will already have all the functionality of each of the fields!

Here’s a simple example of delegating to two separate classes — one encapsulates a person name (first and last) and the other one stores the year of birth. You put these two together and get a more detailed representation of a “person”, which includes their names as well as year of birth:

import groovy.transform.Sortable
import groovy.transform.TupleConstructor

@Sortable
@TupleConstructor
class BeanOne {
    String firstName
    String familyName

    public String getFullName() {
        "$firstName $familyName"
    }
}


import groovy.transform.Sortable
import groovy.transform.TupleConstructor

@Sortable
@TupleConstructor
class BeanTwo {
    int yob

    public int getAge() {
        new Date().year - yob
    }
}


class DelegationBean {
    @Delegate
    BeanOne person

    @Delegate
    BeanTwo dateOfBirth

    static void main(String... args) {
        DelegationBean one = new DelegationBean(person: new BeanOne("Liviu", "Tudor"), dateOfBirth: new BeanTwo(yob: 1975))
        DelegationBean two = new DelegationBean(person: new BeanOne("William", "Shakespeare"), dateOfBirth: new BeanTwo(yob: 1564))

        println one.fullName
        println two.fullName
        println(one < two)
    }
}


As you can see, and as to be expected, the properties from the inner beans are now available on the DelegationBean class. And also we should now expect that our DelegationBean is implementing Sortable — from the beans we are delegating to. But when we call println (one < two), we will get an error looking something like this:

Exception in thread "main" groovy.lang.GroovyRuntimeException: Cannot compare delegation.DelegationBean with value 'delegation.DelegationBean@5762806e' and delegation.DelegationBean with value 'delegation.DelegationBean@17c386de'
at org.codehaus.groovy.runtime.typehandling.DefaultTypeTransformation.compareToWithEqualityCheck(DefaultTypeTransformation.java:603)
at org.codehaus.groovy.runtime.typehandling.DefaultTypeTransformation.compareTo(DefaultTypeTransformation.java:543)
at org.codehaus.groovy.runtime.ScriptBytecodeAdapter.compareTo(ScriptBytecodeAdapter.java:692)
at org.codehaus.groovy.runtime.ScriptBytecodeAdapter.compareLessThan(ScriptBytecodeAdapter.java:712)
at delegation.DelegationBean.main(DelegationBean.groovy:16)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:140)


Well, that sounds like our DelegationBean class actually doesn’t implement Sortable! Let’s verify this to be the case: Replace the println (one < two) with assert one instanceof groovy.transform.Sortable and you will notice that when you run the code again you get:

Exception in thread "main" Assertion failed: 

assert one instanceof Sortable
       |   |
       |   false
       delegation.DelegationBean@12028586

at org.codehaus.groovy.runtime.InvokerHelper.assertFailed(InvokerHelper.java:404)
at org.codehaus.groovy.runtime.ScriptBytecodeAdapter.assertFailed(ScriptBytecodeAdapter.java:650)
at delegation.DelegationBean.main(DelegationBean.groovy:16)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:140)


So what do you know, our class does NOT implement Sortable! Despite the spec for @Delegate clearly stating:

By default, the owner class will also be modified to implement any interfaces implemented by the field. So, in the example above, because Date implements Cloneable the following will be true:

It turns out (try it out) that transformation interfaces (in groovy.transform package) are not included in this.

So let’s change the code now and implement a Comparable interface and handcraft the comparison ourselves in the two bean classes:

package delegation

import groovy.transform.TupleConstructor

@TupleConstructor
class BeanOne implements Comparable<BeanOne> {
    String firstName
    String familyName

    public String getFullName() {
        return "$firstName $familyName"
    }

    @Override
    int compareTo(BeanOne o) {
        if (!o) return false
        if (this.is(o)) return false
        return fullName <=> o.fullName
    }
}


package delegation

import groovy.transform.TupleConstructor

@TupleConstructor
class BeanTwo implements Comparable<BeanTwo> {
    int yob

    public int getAge() {
        new Date().year - yob
    }

    @Override
    int compareTo(BeanTwo o) {
        if (!o) return false
        if (this.is(o)) return false
        return age <=> o.age

    }
}


Now let’s change our DelegatioBean as follows and see what happens:

package delegation

import groovy.transform.Sortable

class DelegationBean {
    @Delegate
    BeanOne person

    @Delegate
    BeanTwo dateOfBirth



    static void main(String... args) {
        DelegationBean one = new DelegationBean(person: new BeanOne("Liviu", "Tudor"), dateOfBirth: new BeanTwo(yob: 1975))
        DelegationBean two = new DelegationBean(person: new BeanOne("William", "Shakespeare"), dateOfBirth: new BeanTwo(yob: 1564))

        assert one instanceof Comparable
        println one.fullName
        println two.fullName
        println(one < two)
    }
}


If you run the above code you will see that the assert statement doesn’t trigger any errors (therefore confirming that now our DelegationBean DOES indeed implement automatically Comparable). However, you get this:

Exception in thread "main" java.lang.ClassCastException: delegation.DelegationBean cannot be cast to delegation.BeanOne
at delegation.DelegationBean.compareTo(DelegationBean.groovy)
at org.codehaus.groovy.runtime.typehandling.DefaultTypeTransformation.compareToWithEqualityCheck(DefaultTypeTransformation.java:592)
at org.codehaus.groovy.runtime.typehandling.DefaultTypeTransformation.compareTo(DefaultTypeTransformation.java:543)
at org.codehaus.groovy.runtime.ScriptBytecodeAdapter.compareTo(ScriptBytecodeAdapter.java:692)
at org.codehaus.groovy.runtime.ScriptBytecodeAdapter.compareLessThan(ScriptBytecodeAdapter.java:712)
at delegation.DelegationBean.main(DelegationBean.groovy:21)


Well what gives? Well it turns out, based on the error, that our automatically provided compareTo() method in our DelegationBean now expects the parameter to be a … BeanOne instance! Why? The answer is in the annotation docco again:

If multiple delegate fields are used and the same method signature occurs in more than one of the respective field types, then the delegate will be made to the first defined field having that signature. If this does occur, it might be regarded as a smell (or at least poor style) and it might be clearer to do the delegation by long hand.

Ok, so this makes sense! This basically says because we are delegating to more than one class implementing Comparable, our class will actually delegate to the first encountered @Delegate annotation — which in our case is BeanOne!

This means that if we run println (one < two.person), we will get true (try it!).

Also, it means that in this case we need to manually craft the DelegationBean comparison method. But in order to do that, we need to get rid of the provided implementation first. And it turns out that the @Delegate annotation provide support for this by using interfaces=false parameter:

    @Delegate(interfaces=false)
    BeanOne person

    @Delegate(interfaces=false)
    BeanTwo dateOfBirth


Now we can handcraft our own compareTo() method in the DelegationBean class and we’re set.

As the docco rightly points out, more than one @Delegate per class is a bit of a code smell — stick to one delegation per class and you’ll be fine. And also remember that the groovy.transform annotations do NOT get carried over.

How do you break a Monolith into Microservices at Scale? This ebook shows strategies and techniques for building scalable and resilient microservices.

Topics:
java ,composition ,delegation ,groovy ,tutorial

Published at DZone with permission of

Opinions expressed by DZone contributors are their own.

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

{{ parent.tldr }}

{{ parent.urlSource.name }}