DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Refcards Trend Reports
Events Video Library
Over 2 million developers have joined DZone. Join Today! Thanks for visiting DZone today,
Edit Profile Manage Email Subscriptions Moderation Admin Console How to Post to DZone Article Submission Guidelines
View Profile
Sign Out
Refcards
Trend Reports
Events
View Events Video Library
Zones
Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks

Modern Digital Website Security: Prepare to face any form of malicious web activity and enable your sites to optimally serve your customers.

Containers Trend Report: Explore the current state of containers, containerization strategies, and modernizing architecture.

Low-Code Development: Learn the concepts of low code, features + use cases for professional devs, and the low-code implementation process.

E-Commerce Development Essentials: Considering starting or working on an e-commerce business? Learn how to create a backend that scales.

Related

  • Transitioning From Groovy to Kotlin for Gradle Android Projects
  • Java String: A Complete Guide With Examples
  • Streamlining Your Workflow With the Jenkins HTTP Request Plugin: A Guide to Replacing CURL in Scripts
  • The Fire From Gods

Trending

  • CodeCraft: Agile Strategies for Crafting Exemplary Software
  • Announcing DZone Core 2.0!
  • What’s New Between Java 17 and Java 21?
  • Apply Strangler Pattern To Decompose Legacy System Into Microservices: Part 1
  1. DZone
  2. Coding
  3. Languages
  4. The Twisted Sisters of Overloadable Groovy Operators

The Twisted Sisters of Overloadable Groovy Operators

Groovy operator overloading is fairly straightforward, but there are some oddities with the ++ and -- operators to be aware of. Also, never return "this". Ever.

Joe Wolf user avatar by
Joe Wolf
·
Jan. 17, 17 · Tutorial
Like (2)
Save
Tweet
Share
8.7K Views

Join the DZone community and get the full member experience.

Join For Free

Groovy supports operator overloading for a limited set of operators. Each supported operator corresponds to a particular method signature. If a type implements that method, then that operator is effectively overloaded for instances of that type.

The + operator, for example, corresponds to the plus method, enabling a.plus(b) to be substituted with a + b. And thanks to the wonders of polymorphism, a type can define multiple plus methods to allow + to behave differently depending on the type on the right hand side of the operator (e.g. [1] + 2 inserts 2 at the end of the list while [1] + [2] joins the two lists together, resulting in [1, 2] for both).

This approach to overloading seemed fairly straightforward until I recently discovered two sister operators that had some interesting twists: ++ and --, which correspond to the next() and previous() methods, respectively.

Twist 1: Implicit Reassignment

The first twist is that when the ++ operator is used on a reference, it also reassigns that reference to the value returned by next(). (The same applies to -- and previous().) This means that, unlike the other overridable Groovy operators, you cannot interchange a standalone statement of a++ with a.next(), as shown by this code.

int i = 0
i++
assert i == 1

int j = 0
j.next() // 1 is returned by next(), but not assigned to j, keeping its value unchanged
assert j == 0 

Twist 2: In Tandem Pre and Post Forms

Like Java, the placement of ++ or -- before or after a reference determines if the reassignment occurs before or after the operation is executed.

int i = 1
int j = ++i // i is incremented, then its value is assigned to j
int k = i++ // k is assigned the current value of i, then i is incremented

assert [i, j, k] == [3, 2, 2]


This holds true for other data types overriding ++ and --, like java.util.Date in the Groovy JDK.

def date = { int dayOfMonth -> new Date(2017 - 1900, Calendar.MAY, dayOfMonth).clearTime() }
Date d = date(1)
Date e = ++d
Date f = d++

assert [d, e, f].date == [3, 2, 2]


Unlike Java, however, Groovy lets you use the operator on both sides of the reference simultaneously (++a++); the corresponding method is called twice, but not in chained succession. Confused? Perhaps the behavior best explained by this code.

class Foo {
    int value
    def next() {
        println "next() called when value is $value"
        new Foo(value + 1)
    }
}

def f = new Foo(1)
++f++ // our wacky tandem usage of ++
println "The final value is $f.value"


When executed, it prints:

next() called when value is 1
next() called when value is 1
The final value is 2


(I'm using Groovy 2.4.7 on Java 1.8.0_111)

Twist 3: A Twist to the Twists

But before we file these caveats under the "good to know" category and move on with our lives, it's worth noting that we can override next() and previous() in such a way that it invalidates the aforementioned behaviors. Specifically, we can return this from our method implementations.

@groovy.transform.TupleConstructor
class Bar {
    int value
    Bar next() {
        ++value
        this // return this instance instead of a new Bar
    }   
}


As a consequence of returning a self-reference from our method, we cause the statements next() and ++ to be perfectly interchangeable.

def b = new Bar(1)
b++
assert b.value == 2
b.next()
assert b.value == 3


Furthermore, we can use the in-tandem operators and get the expected result (insomuch as you can expect in-tandem operators to effect a result at all).

def b = new Bar(1)
++b++
assert b.value == 3


Is This the Answer?

So are we to conclude that it's better to return this than a new instance from our next() and previous() methods? Certainly not!

For starters, assuming that ++ or -- is supposed to change something about the object it's called on, returning this must mean that the object's type is mutable. Who wants to write mutable data types these days?

Secondly, if we return this from our method, we break the ++a vs. a++ reassignment behavior, making the placement of the operator moot. Consider how our Bar type compares to int and java.util.Date when applying ++ in its pre and post forms.

def b = new Bar(1)
def c = ++b
def d = b++

assert [b, c ,d].value == [3, 3, 3] // not [3, 2, 2] as with int and java.util.Date


All three Bar references have the same value because they're all pointing to the same instance of Bar.

Summary

Groovy operator overloading is fairly straightforward, but there are some oddities with the ++ and -- operators to be aware of. Given that 99% of all custom data types outside of numbers and iterators will have no reasonable semantics for the next() and previous() methods, and given that there is a 0% need to ever write a statement like ++a++, you will likely never encounter these oddities. The important takeaway is that, on the rare occassions when you do decide to implement next() and previous() in order to override ++ and --, never return this.

Operator (extension) Groovy (programming language)

Opinions expressed by DZone contributors are their own.

Related

  • Transitioning From Groovy to Kotlin for Gradle Android Projects
  • Java String: A Complete Guide With Examples
  • Streamlining Your Workflow With the Jenkins HTTP Request Plugin: A Guide to Replacing CURL in Scripts
  • The Fire From Gods

Comments

Partner Resources

X

ABOUT US

  • About DZone
  • Send feedback
  • Careers
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 100
  • Nashville, TN 37211
  • support@dzone.com

Let's be friends: