Wednesday, April 04, 2012

Xtend Example : Working with Distances


This is an example showing how to do arithmetics with distances in Xtend. It uses operator overloading and extension methods
Here’s what we want to be able write:
class HelloDistance {
  @Test def void testOperators() {
    assertEquals( 23.cm , 10.mm + 22.cm )
    assertEquals( 3.km , 1.km * 3 )
    assertEquals( 1_019_99_7.mm, 1.km + 20.m - 3.mm )
  }
}
The first thing we need to do in order to be able to create distances like shown in the example above, is to define extension methods for the type int.
We create a new class, call it Distance and add the following static methods:
class Distance {
  def static mm(int millimeters) {
    new Distance(millimeters) // not yet existing
  }
  def static cm(int centimeters) {
    mm(centimeters * 10)
  }
  def static m(int meters) {
    cm(meters * 100)
  }
  def static km(int kilometers) {
    m(kilometers * 1000)
  }
}
In order to add the static methods as extensions to the type int, we have to add the following import declaration in our test class:
import static extension my.example.Distance.* 
As you can see we call a not yet existing constructor which takes the distance in millimeters as an int. So let’s add the missing constructor and add the corresponding field to the Distance class:
int mm
new(int mm) {
  this.mm = mm
}
Since this is an immutable value object we might also want to have a proper equals and hashcode implementation:
override equals(Object obj) {
  switch obj {
    Distance : this.mm == obj.mm
    default : super.equals(obj) 
  }
}
override hashCode() {
  this.mm
} 
Finally we need to add definitions for the arithmetic operators we want to use:
def operator_plus(Distance other) {
  return new Distance(this.mm + other.mm)
}
def operator_minus(Distance other) {
  return new Distance(this.mm - other.mm)
}
def operator_multiply(int times) {
  return new Distance(this.mm * times)
}
That’s it. It’s just API definition, and of course since Xtend is statically typed the tooling will guide you when using it.